Contenido


Aspectos básicos de Hyperledger Composer, Parte 3

Despliegue de forma local, interactúe y extienda su red blockchain

Cómo explorar más herramientas, UIs y seguridad de Hyperledger Composer.

Comments

Contenido de la serie:

Este contenido es la parte # de # de la serie: Aspectos básicos de Hyperledger Composer, Parte 3

Manténgase en contacto por contenidos adicionales de esta serie.

Este contenido es parte de la serie:Aspectos básicos de Hyperledger Composer, Parte 3

Manténgase en contacto por contenidos adicionales de esta serie.

Este tutorial se realiza sobre la Parte 1, donde aprendió cómo se crea y se prueba una sencilla red empresarial en una versión local de Hyperledger Composer Playground. En la Parte 2vio cómo se modela un sensor GPS de IoT en un contenedor de transporte añadiendo las lecturas del GPS al activo del Envío, y cómo se modifica el chaincode para enviar una alerta cuando el Envío llega a su puerto de destino. Después, usted desplegó la muestra Red de Alimentos Perecederos en Composer Online Playground de IBM Cloud.

Ahora, en esta tercera parte final, instalará Hyperledger Fabric en su computadora, y desplegará el archivo de redes empresariales (BNA) en una instancia de Hyperledger Fabric que se ejecute en su máquina (que se conoce como su Hyperledger Fabric local). Usted instalará más herramientas y generará una interfaz REST basada en Loopback que se puede utilizar para interactuar con la aplicación de blockchain de muestra de la red.

La Parte 3 también incluye una discusión detallada de los conceptos de seguridad de Hyperledger Composer. Esta parte concluye con los pasos necesarios para contarlo todo y para extender iot-perishable-network para crear una versión más "real" de la red de Alimentos Perecederos.

Requisitos previos

Asumiré que ya haya superado la Parte 2 y que tiene el siguiente software instalado en su computadora:

1

Despliegue la red en su Hyperledger Fabric local

Hyperledger Fabric es una infraestructura para construir aplicaciones de blockchain con fines empresariales, y como ya ha aprendido, Hyperledger Composer es un complemento que facilita la construcción de aplicaciones de blockchain que se ejecutan en Hyperledger Fabric. Hasta ahora, usted ha usado Composer Playground en modo "sólo navegador" (en la Parte 1) y Composer Online Playground de IBM Cloud (Parte 2).

Ahora, instalará y ejecutará Hyperledger Fabric en su computadora, y utilizará la Interfaz de la Línea de Comando de Composer (CLI) para interactuar con ella.

1a

Obtenga Hyperledger Fabric

Primero, en su computadora tiene que crear un directorio en el que quiera realizar el desarrollo local de Hyperledger Composer. Para este tutorial, en el código me referiré a esta ubicación como $COMPOSER_ROOT , y en la prosa me referiré como su directorio raíz de Composer. Este directorio puede estar donde usted quiera, pero le recomiendo que lo cree justo fuera de su carpeta de inicio, y que siempre establezca la variable de entorno COMPOSER_ROOT para ese directorio en el shell actual, ya que así es como me referiré a esa ubicación a lo largo de este tutorial.

Ahora, descargue el archivo de la distribución fabric-dev-servers.zip en su directorio raíz de Composer utilizando el comando curl , y descomprímalo. La secuencia de comandos es parecida a esta (suponiendo que ~/HyperledgerComposer es el directorio raíz de este ejemplo):

                mkdir ~/HyperledgerComposer
                export COMPOSER_ROOT=~/HyperledgerComposer
                cd $COMPOSER_ROOT
                mkdir fabric-dev-servers && cd fabric-dev-servers
                curl -O https://raw.githubusercontent.com/hyperledger/composer-tools/master/packages/fabric-dev-servers/fabric-dev-servers.zip
                unzip fabric-dev-servers.zip
1b

Inicie Hyperledger Fabric

Nota: antes de continuar, asegúrese de que Docker se está ejecutando.

La primera vez que ejecute Hyperledger Fabric, ejecute esta secuencia de comandos:

                cd $COMPOSER_ROOT/fabric-dev-servers
                ./downloadFabric.sh
                ./startFabric.sh
                ./createPeerAdminCard.sh

Tardará varios minutos en exportar el primer comando, que extrae todas las imágenes necesarias de Docker. El segundo comando inicia el Hyperledger Fabric local. El último comando envía una tarjeta ID para el administrador del tejido, es PeerAdmin. No se preocupe todavía con eso; las tarjetas de ID se cubrirán más tarde en este tutorial. La salida del script createPeerAdminCard.sh se parece a esto:

                $ ./createPeerAdminCard.sh 
                Development only script for Hyperledger Fabric control
                Running 'createPeerAdminCard.sh'
                FABRIC_VERSION is unset, assuming hlfv1
                FABRIC_START_TIMEOUT is unset, assuming 15 (seconds)
                
                Using composer-cli at v0.15.0
                Successfully created business network card to /tmp/PeerAdmin@hlfv1.card
                
                Command succeeded
                
                Successfully imported business network card: PeerAdmin@hlfv1
                
                Command succeeded
                
                Hyperledger Composer PeerAdmin card has been imported
                The following Business Network Cards are available:
                
                
                ┌─────────────────┬───────────┬─────────┐
                │ CardName        │ UserId    │ Network │
                ├─────────────────┼───────────┼─────────┤
                │ PeerAdmin@hlfv1 │ PeerAdmin │         │
                └─────────────────┴───────────┴─────────┘
                
                Issue composer card list --name <CardName>  to get details of the card
                
                Command succeeded

Tome nota del nombre de la tarjeta, porque la necesitará para ejecutar todos los comandos CLI de este tutorial.

Sólo tendrá que ejecutar el script createPeerAdminCard.sh una vez. Desde ahora, para iniciar Hyperledger Fabric, sólo tendrá que ejecutar el script startFabric.sh de esta manera:

                cd $COMPOSER_ROOT/fabric-dev-servers
                ./startFabric.sh

Cuando el script acabe, Hyperledger Fabric estará ejecutándose en su computadora. Para apagar el Hyperledger Fabric local, ejecute el script stopFabric.sh.

Debido a este tutorial, le sugiero que por ahora deje Hyperledger Fabric en funcionamiento.

1c

Despliegue en Hyperledger Fabric

En la Parte 2, usted clonó el proyecto developerWorks de GitHub y modificó la red de Alimentos Perecederos. En el directorio developerWorks/iot-perishable-network le proporcionó una versión acabada de esa red. Usted utilizará esa red para el resto de este tutorial.

Para desplegar iot-perishable-network en su Fabric local, utilice Composer CLI y ejecute esta secuencia de comandos:

                cd $COMPOSER_ROOT/developerWorks/iot-perishable-network
                composer network deploy -a dist/iot-perishable-network.bna -A admin -S adminpw -c PeerAdmin@hlfv1 -f networkadmin.card
                composer card import --file networkadmin.card

El comando composer network deploy despliega el archivo de red especificado (dist/iot-perishable-network.bna) en Hyperledger Fabric local, y para autentica utiliza la tarjeta PeerAdmin que creó anteriormente cuando ejecutó el script createPeerAdminCard.sh. Cuando el despliegue haya acabado se emitirá una tarjeta ID para el administrador de la red, cuyas credenciales — , es decir id de usuario y contraseña (o secreto) — están almacenadas en el archivo networkadmin.card.

El resultado se parece a esto:

                $ composer network deploy -a dist/iot-perishable-network.bna -A admin -S adminpw -c PeerAdmin@hlfv1 -f networkadmin.card
                Deploying business network from archive: dist/iot-perishable-network.bna
                Business network definition:
                	Identifier: iot-perishable-network@0.1.12
                	Description: Shipping Perishable Goods Business Network
                
                ✔ Deploying business network definition. This may take a minute...
                Successfully created business network card to networkadmin.card
                
                Command succeeded

El comando composer card import dice a Hyperledger Composer que importe el archivo de la tarjeta específica para que se pueda utilizar posteriormente para autenticar el usuario cuyas credenciales están almacenadas en la tarjeta. En este caso, la tarjeta es para el administrador de la red. Más tarde hablaré acerca de las tarjetas. El resultado se parece a esto:

                $ composer card import --file networkadmin.card 
                Successfully imported business network card: admin@iot-perishable-network
                
                Command succeeded
2

Interactúe con blockchain a través de la interfaz Composer REST

Usted ha desplegado iot-perishable-network en Hyperledger Fabric local, pero ¿cómo sabe lo que está ahí? ¿Cómo interactúa con ello? Hyperledger Composer brinda una herramienta que se llama composer-rest-server y que genera una interfaz REST basada en Loopback para acceder a la red.

2a

Instale el generador interfaces Composer REST

Primero, es necesario instalar el generador de loopback composer-rest-server. Vaya a la línea de comando (Ubuntu) o abra una ventana del terminal (MacOS), e introduzca este comando

npm install -g composer-rest-server

Cuando la instalación acabe, verifique que composer-rest-server se instaló correctamente, ejecutando composer-rest-server -v:

                $ composer-rest-server -v
                v0.15.0
2b

Genere la interfaz de REST

Asegúrese de que Hyperledger Fabric se está ejecutando y que iot-perishable-network se despliega antes de generar la interfaz REST (o no habrá nada a lo que conectarse). Desde la línea de comando, ejecute el comando composer-rest-server. Le solicitará alguna información. Verá algo como esto:

Listado 1. Cómo iniciar el servidor REST
                $ composer-rest-server
                ? Enter the name of the business network card to use: admin@iot-perishable-network
                ? Specify if you want namespaces in the generated REST API: always use namespaces
                ? Specify if you want to enable authentication for the REST API using Passport: No
                ? Specify if you want to enable event publication over WebSockets: No
                ? Specify if you want to enable TLS security for the REST API: No
                
                To restart the REST server using the same options, issue the following command:
                   composer-rest-server -c admin@iot-perishable-network -n always
                
                Discovering types from business network definition ...
                Discovered types from business network definition
                Generating schemas for all types in business network definition ...
                Generated schemas for all types in business network definition
                Adding schemas for all types to Loopback ...
                Added schemas for all types to Loopback
                Web server listening at: http://localhost:3000
                Browse your REST API at http://localhost:3000/explorer

El servidor REST tiene que saber cómo se autentica con Hyperledger Fabric, para que se pueda comunicar con la red empresarial. Para ello, se proporciona una tarjeta de ID (línea 2) como la tarjeta ID admin@iot-perishable-network de comienzos de este tutorial.

Los espacios de nombres ayudan a evitar conflictos con los nombres. En iot-perishable-network, esto no es un gran problema, ya que sólo hay un puñado de activos, participantes y transacciones. Creo que utilizar siempre los espacios de nombres (línea 3) es una buena idea. Probar las redes de mundo real que tienen muchos participantes, activos y transacciones es suficientemente duro; usar espacios de nombres le ayudará a evitar los problemas relacionados con conflictos entre nombres.

Cuando utilice la interfaz REST para realizar pruebas, por ahora no se preocupe con la autenticación (línea 4), de eventos ni de WebSockets (línea 5), ni de la seguridad TLS (línea 6). Sin embargo, si decide desplegar el servidor REST como parte de su solución blockchain de producción, asegúrese de que verifica los enlaces aquí o en los Temas relacionados de la parte final de este tutorial.

La próxima vez que quiera instalar el servidor REST con las mismas opciones, simplemente utilice el comando que se muestra (línea 9) y ¡sáltese la entrevista!

2c

Utilice la interfaz REST

Para utilizar la interfaz REST, abra un navegador y apúntelo a la dirección que se muestra en la línea 18 del Listado 1. Verá algo como esto:

Figura 1. La interfaz REST
The REST interface
The REST interface

La interfaz es bastante intuitiva. Para trabajar con objeto, haga clic en él, y cuando se expanda verá los métodos REST que puede invocar (por ejemplo, /GET, /POST, etc.). Para invocar la transacción SetupDemo (que instancia el modelo empresarial), haga clic en la línea que contiene SetupDemo, que después se expande hacia el método POST. Haga clic en el botón ¡Pruébelo! para invocar la transacción. Si tiene éxito, verá un código de respuesta HTTP 200. Después, es posible navegar a lo largo del modelo y ver los diferentes objetos, como el Contrato que se muestra en la Imagen 2.

Figura 2. La interfaz REST que muestra el objeto Contrato
The REST interface showing the Contract object
The REST interface showing the Contract object

En el video Le mostraré en detalle cómo se utiliza la interfaz REST, así que, asegúrese de echarlo un vistazo.

2d

Proteja la interfaz REST

Esto se encuentra más allá del ámbito de este tutorial, pero sí tiene planes para ejecutar la interfaz REST en producción, necesita una estrategia para tratar con ello. ¡Sólo para este tema haría falta un tutorial completo! Afortunadamente, para ayudar hay recursos en el sitio web de Hyperledger Composer. Aquí hay algunos enlaces para los documentos de Composer:

Para su comodidad, esos enlaces también se encuentran en Temas relacionados de la parte final del tutorial.

Video: Cómo utilizar la interfaz de Composer REST

3

Configurar la seguridad de Hyperledger Composer

En Hyperledger Composer hay dos niveles de seguridad:

  • Administrador de Hyperledger Fabric
  • Administrador de red empresarial

El administrador de su Hyperledger Fabric local es el Administrador de Pares (o PeerAdmin para abreviar), que usted creó cuando instaló el Hyperledger Fabric local. Toda red empresarial también debería tener un administrador, que se crea cuando un administrador de Hyperledger Fabric despliega la red. La autentificación de ambos se maneja a través de tarjetas de ID, que es lo siguiente que aprenderá.

3a

Tarjetas de ID

Una tarjeta de ID (o tarjeta para abreviar) es una colección de archivos que contiene toda la información necesaria para permitir que un participante se conecte a una red empresarial. La tarjeta es conocida como identidad. Antes de utilizarla debe ser emitida para el usuario, lo que le permitirá ser autenticado y autorizado para utilizar la red. Las tarjetas son una forma muy útil de proteger el acceso a una red de Hyperledger Fabric. En vez de continuar con una contraseña (llamada secreto en terminología de Hyperledger Composer), usted importa la tarjeta dentro de una colección de tarjetas de Hyperledger Fabric llamada cartera. Desde ese momento, simplemente se puede consultar la tarjeta para autenticar esa identidad.

La forma general de especificar una tarjeta es: userid@network, donde userid es el id único del usuario, y el blockchain es la red en la que el usuario está autenticado.

Para manejar los dos niveles de seguridad de Hyperledger Composer, usted necesitará una tarjeta para al menos: (1) el PeerAdmin y (2) el administrador de la red empresarial.

PeerAdmin

La tarjeta de PeerAdmin es una tarjeta de ID especial que se utiliza para administrar el Hyperledger Fabric local. En una instalación de desarrollo, como la de su computadora, la tarjeta ID de PeerAdmin se crea cuando se instala el Hyperledger Fabric local.

La forma de una tarjeta de PeerAdmin para una red de Hyperledger Fabric v1.0 es PeerAdmin@hlfv1. Usted ya ha utilizado esta tarjeta antes en el tutorial cuando desplegó iot-perishable-network en su Hyperledger Fabric local.

En general, el PeerAdmin es un rol especial que se ha reservado para funciones como:

  • Desplegar redes empresariales
  • Crear, emitir y revocar tarjetas de ID para administradores de redes empresariales

Como desarrollador, en una instalación de Hyperledger Fabric de producción, usted no tendría acceso a la tarjeta de PeerAdmin. En vez de eso, el administrador de Hyperledger Fabric desplegaría su red empresarial, crearía las tarjetas de ID, etc. Cuando desarrolle y pruebe redes blockchain utilizando su Hyperledger Fabric local, utilizará la tarjeta de ID de PeerAdmin para realizar esas funciones.

Administrador de red empresarial

Cuando el PeerAdmin despliegue su red en Hyperledger Fabric, se emitirá una tarjeta de ID para el administrador de la red empresarial, y después, esta tarjeta se utilizará cuando el administrador de la red empresarial necesite hacer algo con la red empresarial, como utilizar la Interfaz de la Línea de Comando de Composer (lo que hará en breve).

¿Se acuerda de la tarjeta admin@iot-perishable-network de antes? Esa era la tarjeta del administrador de la red empresarial que el PeerAdmin (es decir, el administrador de Hyperledger Fabric local) emitió cuando se desplegó iot-perishable-network.

En general, el administrador de la red empresarial es un rol especial que se ha reservado para funciones como:

  • Actualizar la red empresarial que se está ejecutando
  • Consultar los diferentes registros (participante, identidad y así sucesivamente)
  • Crear, emitir y revocar tarjetas de ID para los participantes de la red empresarial

Es verdad, la tarjeta de ID de administrador también se puede utilizar para emitir otras tarjetas de ID de participantes específicos (demostraré cómo hacerlo más tarde en el tutorial), para que todos los participantes tengan sus propias tarjetas de ID. El acceso a la red se puede controlar con esas tarjetas.

3b

Control de Acceso

Hablando de control de acceso, Composer implementa el concepto de seguridad basada en roles a través de permisos que se han incluido directamente en su arquitectura para manejar la autenticación y la autorización. El acceso de un participante a los recursos se controla en base a la entidad que se ha emitido para ese participante.

En esta sección, verá cómo configurar las reglas de control de acceso para que el participante asegure los recursos de la red a través del archivo de la Lista de Control de Acceso (ACL) llamado permissions.acl (en el momento de escribir esto, el archivo debe tener este nombre). Estos son algunos de los puntos destacados:

  • El acceso se aplica a otorgar o denegar basándose en recursos que coinciden con la regla. De forma predeterminada, si un recurso no coincide con ninguna regla, su acceso es denegado.
  • El archivo se procesa de arriba hacia abajo, así que se aplica la primera regla que garantice o deniegue el acceso a un recurso específico, y no puede ser anulada por una regla posterior.
  • El formato de la regla es bastante intuitivo. La palabra clave rule (regla) indica el inicio de una regla, seguida por un nombre de regla, que debe ser único.
  • La regla está formada por un conjunto de pares de nombre/valor que definen las propiedades de la regla.

El Listado 2 muestra el archivo permissions.acl de la red empresarial iot-perishable-network.

Listado 2. permissions.acl de iot-perishable-network
                /**
                 * Lista de control de acceso de muestra.
                 */
                rule Default {
                    description: "Allow all participants access to all resources"
                    participant: "ANY"
                    operation: ALL
                    resource: "org.acme.shipping.perishable.*"
                    action: ALLOW
                }
                
                rule SystemACL {
                  description:  "System ACL to permit all access"
                  participant: "org.hyperledger.composer.system.Participant"
                  operation: ALL
                  resource: "org.hyperledger.composer.system.**"
                  action: ALLOW
                }

                rule NetworkAdminUser {
                  description: "Grant business network administrators full access to user resources"
                    participant: "org.hyperledger.composer.system.NetworkAdmin"
                    operation: ALL
                    resource: "**"
                    action: ALLOW
                }

                rule NetworkAdminSystem {
                    description: "Grant business network administrators full access to system resources"
                    participant: "org.hyperledger.composer.system.NetworkAdmin"
                    operation: ALL
                    resource: "org.hyperledger.composer.system.**"
                    action: ALLOW
                }

El formato general de todas las propiedades es property: "MATCH_EXPRESSION". Las propiedades son:

description— (descripción) un nombre para la regla, legible por los humanos y escrito en comillas dobles. Ejemplo: description: "Esto es una descripción"

participant— (participante) nombre completo del Participante al que se otorga o deniega el acceso, escrito entre comillas dobles. En MATCH_EXPRESSION se pueden especificar varios participantes a través del uso del carácter comodín de asterisco único (*) para indicar "todos", asterisco doble (**) para indicar recursión dentro de un espacio de nombres, o de ANY, empareja todos los participantes de todos los espacios de nombres.

Ejemplos:

  • participant: "org.acme.shipping.perishable.Grower"— Aplica la regla solo a org.acme.shipping.perishable.Grower.
  • participant: "org.acme.shipping.perishable.*"— Aplica la regla a todos los participantes del espacio de nombres org.acme.shipping.perishable.
  • participant: "org.acme.shipping.**"— Aplica la regla a todos los participantes del espacio de nombres org.acme.shipping, y recursivamente a todos los espacios de nombres que están por debajo.
  • participant: "ANY"— Aplica la regla a todos los participantes de todos los espacios de nombres.

operation— la MATCH_EXPRESSION puede ser una o más de las siguientes: CREATE, READ, UPDATE, DELETEo ALL. Cuando son varios valores éstos se pueden separar por comas (por ejemplo, CREATE,READ otorgará al recurso los accesos CREATE y READ ). Utilice ALL por sí mismo para indicar que la regla se aplica a todas las operaciones.

Ejemplos:

  • operation: ALL— Aplica la regla a todas las operaciones CRUD.
  • operation: CREATE— Aplica la regla solo a la operación CREATE . operation:
  • READ,UPDATE— Aplica la regla a las operaciones READ y UPDATE .

resource— define la "cosa" a la que se aplica la regla. El recurso puede ser de cualquier clase (es decir, un activo, un participante o una transacción) del modelo empresarial. También se pueden especificar varias clases a través del uso de caracteres comodín, igual que se hizo anteriormente para los participantes.

Ejemplos:

  • resource: "org.acme.shipping.perishable.TemperatureReading"— La regla sólo se aplica a la transacción org.acme.shipping.perishable.TemperatureReading.
  • resource: "org.acme.shipping.perishable.*"— La regla se aplica a todas las clases del espacio de nombres org.acme.shipping.perishable.
  • resource: "org.acme.shipping.**"— Aplica la regla a todos los recursos del espacio de nombres org.acme.shipping, y recursivamente a cualquier espacio de nombres que esté por debajo.

action— (acción) la acción que se aplica cuando se lanza la regla. Una de las siguientes: ALLOW para otorgar acceso o DENY para denegar a los participantes específicos el acceso a los recursos.

Ejemplos:

  • action: ALLOW— Permite el acceso a los recursos especificados.
  • action: DENY— Deniega el acceso a los recursos especificados.

Nota: Si su red no tiene permissions.acl, entonces el acceso se otorga a todos los participantes (es decir, el acceso está completamente abierto — no hay seguridad a nivel de recursos).

Verá cómo se utilizan las reglas más tarde en el tutorial. La documentación de Composer ACL también tiene muchos ejemplos, así que asegúrese de echarla un vistazo.

Emitir un ID nuevo - Playground

Las tarjetas de ID pueden ser un poco abstractas y difíciles de visualizar. Así que, vamos a echar un vistazo a una en Playground (de hecho, usted ya lo ha hecho, aunque es posible que no se haya dado cuenta).

Inicie Hyperledger Composer Playground e importe iot-perishable-network (no se olvide de crear el BNA ejecutando npm install si todavía no lo ha hecho). Usted ya importó una red en Parte 2, así que échelo un vistazo si necesita refrescar la memoria.

Asegúrese de que invoca la transacción SetupDemo para crear los participantes y para almacenarlos en el registro de participantes. ¿Por qué? Por que no se puede emitir una tarjeta de ID para un participante que no esté en el registro de participantes. Para trabajar con las clases del modelo empresarial, es necesario instanciar el modelo, y eso es lo que SetupDemo hace por usted. Revise la sección "Probar la red empresarial" de la Parte 1 de esta serie de tutoriales si quiere obtener más información sobre el trabajo con modelos empresariales.

Una vez que haya distanciado del modelo, haga clic en la esquina donde ve admin para expandir la lista desplegable y para seleccionar Registro de ID. En la siguiente pantalla, elija Emitir ID nuevo y luego introduzca grower1 como el Nombre del ID, y seleccione farmer@email.com de la lista desplegable. Vea la Imagen 3.

Figura 3. Emitir una tarjeta de ID nueva en Playground
Issue a new ID Card in Playground
Issue a new ID Card in Playground

Haga clic en el botón Crear Nuevo para permitir el ID. Una vez que la tarjeta de ID de grower1 haya sido emitida, se mostrará en el registro de ID. Vea la Imagen 4.

Figura 4. Tarjeta de ID nueva de grower1 en Playground
New grower1 ID card in Playground
New grower1 ID card in Playground

Más tarde en este tutorial le mostraré cómo se emiten tarjetas ID con Composer CLI.

4

Interactuar con la red a través de Composer CLI

En la Parte 2 he mostrado cómo se instala Composer Command Line Interface (CLI), y usted lo utilizó antes en este tutorial para desplegar iot-perishable-network e importar la tarjeta admin@iot-perishable-network. Ahora usted utilizará el CLI para interactuar con la red empresarial iot-perishable-network. El CLI es muy útil porque permite su uso en scripts, y es fácil de usar, como veremos más adelante. Para utilizar el CLI, usted necesitará ir a la línea de comando (Ubuntu) o abrir una ventana de terminal (MacOS).

Haga un ping a la red

Una vez que haya desplegado la red iot-perishable-network, podrá enviarla un ping, que no tiene ningún efecto secundario. Es una forma segura de ver si su red está funcionando. Introduzca este comando:

composer network ping --card admin@iot-perishable-network

Si el subcomando ping tiene éxito, usted verá un resultado como este:

                $ composer network ping --card admin@iot-perishable-network
                The connection to the network was successfully tested: iot-perishable-network
                	version: 0.15.0
                	participant: org.hyperledger.composer.system.NetworkAdmin#admin
                
                Command succeeded

Invocar la transacción SetupDemo

La transacción SetupDemo se utiliza para instanciar el modelo. Introduzca este comando:

                composer transaction submit --card admin@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.SetupDemo"}'

El resultado se parece a esto:

                $ composer transaction submit --card admin@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.SetupDemo"}'
                Transaction Submitted.
                
                Command succeeded

Invocar la transacción TemperatureReading

El sensor IoT invoca la transacción TemperatureReading en el contenedor de envío cada vez que se realiza una lectura de temperatura y ésta tiene que ser registrada en blockchain. Introduzca este comando:

                composer transaction submit --card admin@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 0, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'

El resultado se parece a esto:

                $ composer transaction submit --card admin@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 0, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Invocar la transacción ShipmentReceived

El Importador invoca la transacción ShipmentReceived cuando se recibe el envío. Introduzca este comando:

                composer transaction submit --card admin@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.ShipmentReceived", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'

El resultado se parece a esto:

                $ composer transaction submit --card admin@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.ShipmentReceived", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Actualizar la redk

Esto es útil cuando se hace un cambio durante el desarrollo y cuando es necesario actualizar la red que se ha desplegado. Después de que ejecute npm install para reconstruir el BNA, introduzca este comando:

                composer network update -a dist/iot-perishable-network.bna --card admin@iot-perishable-network

El resultado se parece a esto:

                $ composer network update -a dist/iot-perishable-network.bna --card admin@iot-perishable-network
                Deploying business network from archive: dist/iot-perishable-network.bna
                Business network definition:
                	Identifier: iot-perishable-network@0.1.12
                	Description: Shipping Perishable Goods Business Network
                
                ✔ Updating business network definition. This may take a few seconds...
                Successfully created business network card to undefined
                
                Command succeeded

Asegúrese de que echa un vistazo a la documentación de CLI para ver una referencia completa de los comandos, ejemplos, y muchas más cosas.

5

Juntarlo todo

Todo lo que ha aprendido hasta ahora ha sido la construcción hasta este punto: Ahora, usted puede modificar iot-perishable-network para convertirlo en una aplicación blockchain más realista. Esto es lo que usted hará en esta sección:

  • Haga cambios en el modelo de red:
    • Añada participantes nuevos para representar a los sensores de IoT en el contenedor de transporte
    • Añada transacciones nuevas para crear un modelo más realista de un flujo de trabajo de transporte
    • Añada eventos nuevos que se trasmiten cuando se alcanzan determinados puntos del flujo de trabajo
  • Añada chaincode para las transacciones nuevas
  • Modifique los permisos de red
  • Añada las pruebas de las funciones de Cucumber a las pruebas unitarias de sus cambios
  • Construya la red y ejecute las pruebas unitarias
  • Despliegue la red en su Hyperledger Fabric local
  • Emita IDs para el Productor, los sensores de GPS y de Temperatura, el Transportista y el Importador
  • Ejecute transacciones a través del CLI

Cuando haya acabado de modificar iot-perishable-network, habrá aprendido las habilidades básicas que se necesitan para utilizar Hyperledger Composer, estará listo para acometer un proyecto de una aplicación blockchain del mundo real, será la envidia de todos sus amigos, e historias de su proceso de Hyperledger Composer y Fabric se propagarán a lo largo y ancho del mundo (vale, es posible que con las dos últimas haya exagerado).

Si en algún momento se queda bloqueado y necesita un poco de ayuda, asegúrese de que verifica el código de la solución, que es un proyecto llamado iot-perishable-network-advanced que usted ya clonó en su computadora cuando clonó el proyecto developerWorks de GitHub.

Nota: Puede utilizar cualquier editor con el que esté cómodo, pero yo utilizaré VSCode (que usted instaló en la Parte 2) para todos los cambios de esta sección, y las siguientes instrucciones lo reflejarán. Si usted no está utilizando VSCode, adáptelo en consecuencia.

5a

Realice cambios en el modelo

Para realizar los cambios anteriores, es necesario que inicie el modelo. Abra el archivo del modelo models/perishable.cto en VSCode y realice los cambios siguientes. Nota: he omitido los comentarios de los fragmentos de códigos siguientes sólo para ahorrar espacio en este tutorial. Si echa un vistazo a iot-perishable-network-advanced, verá que he añadido comentarios para cada elemento que he añadido en el modelo, y le recomiendo que haga lo mismo.

Primero, modifique la transacción ShipmentReceived para añadir una propiedad opcional llamada receivedDateTime, y después añada tres transacciones nuevas que amplían ShipmentTransaction para actualizar el blockchain (libro contable) cuando el envío (1) haya sido empaquetado, (2) haya sido recogido para su carga y (3) haya ha sido cargado en el portacontenedores:

                transaction ShipmentReceived extends ShipmentTransaction {
                  o DateTime receivedDateTime optional
                }
                
                transaction ShipmentPacked extends ShipmentTransaction {
                }
                
                transaction ShipmentPickup extends ShipmentTransaction {
                }
                
                transaction ShipmentLoaded extends ShipmentTransaction {
                }

Después, modifique el activo Shipment para añadir cuatro propiedades nuevas en las transacciones relacionadas con el envío, para que cuando se ejecuten esas transacciones se almacenen en el blockchain con Shipment. Las siguientes líneas destacadas son las que se tienen que añadir (líneas 9-12).

                asset Shipment identified by shipmentId {
                  o String shipmentId
                  o ProductType type
                  o ShipmentStatus status
                  o Long unitCount
                  --> Contract contract
                  o TemperatureReading[] temperatureReadings optional
                  o GpsReading[] gpsReadings optional
                  o ShipmentPacked shipmentPacked optional
                  o ShipmentPickup shipmentPickup optional
                  o ShipmentLoaded shipmentLoaded optional
                  o ShipmentReceived shipmentReceived optional
                }

Ahora añada el participante abstracto para representar un dispositivo de IoT que es identificado por una propiedad "String" llamada deviceId, y añada dos de sus subclases: una para representar un sensor de temperatura, y otra para representar un sensor GPS. Esos participantes actualizarán el blockchain con sus respectivas lecturas. ¿Los dispositivos son participantes de la red? Sí; los participantes de una red blockchain no tienen por qué ser seres humanos.

                abstract participant IoTDevice identified by deviceId {
                  o String deviceId
                }
                
                participant TemperatureSensor extends IoTDevice {
                }
                
                participant GpsSensor extends IoTDevice {
                }

Finalmente, en una aplicación real de blockchain, los eventos se emitirían en todos los puntos importantes del flujo de trabajo: cuando se empaqueta el envío, cuando se recoge, etc. Añada cuatro elementos nuevos para representar esos puntos del flujo de trabajo del envío.

                event ShipmentPackedEvent {
                  o String message
                  --> Shipment shipment
                }
                
                event ShipmentPickupEvent {
                  o String message
                  --> Shipment shipment
                }
                
                event ShipmentLoadedEvent {
                  o String message
                  --> Shipment shipment
                }

                event ShipmentReceivedEvent {
                  o String message
                  --> Shipment shipment
                }

Y esos son todos los cambios en el modelo. Después, es necesario realizar algunas modificaciones en las transacciones existentes de JavaScript, y añadir el chaincode para las transacciones nuevas.

5b

Añadir el chaincode de las transacciones

Hasta ahora, todo el chaincode de las redes con las que ha trabajado estaba contenido en lib/logic.js. Obviamente, una aplicación blockchain del mundo real tendría muchas transacciones, lo que causaría que logic.js estuviera abarrotado, y eventualmente sería difícil de mantener.

Hasta ahora, todo el código de su transacción estaba en el directorio lib de su modelo, e Hyperledger Composer no ha tenido ningún problema para resolver las llamadas a las funciones. Para ilustrar esto, usted realizará algunos cambios a logic.js y añadir a algunos archivos de origen de JavaScript más.

En VSCode, haga clic derecho en el directorio lib y elija Nuevo Archivo, y después introduzca instantiateModelForTesting.js como el nombre del archivo. Ahora, péguelo en la siguiente cabecera, o el paso "verificación de licencia" de la compilación fallará, lo que le proporcionará un error cuando compile el código más tarde:

Listado 3. La cabecera de la licencia de Apache 2.0. Asegúrese de que pega esto en todos los archivos de JavaScript nuevos que cree en su proyecto.
                /*
                 * Licencia bajo los términos de la Apache License, Version 2.0 (la "Licencia");
                 * este archivo no se puede utilizar salvo en conformidad con la Licencia.
                 * Puede obtener una copia de la Licencia en
                 *
                 * http://www.apache.org/licenses/LICENSE-2.0
                 *
                 * Salvo que la ley correspondiente obligue o si está acordado por escrito, el software
                 * que se distribuyen bajo la Licencia se distribuye "COMO ESTÁ",
                 * SIN GARANTÍAS NI CONDICIONES DE NINGÚN TIPO, ni implícitas ni explícitas.
                 * Vea la Licencia para los permisos específicos que gobiernan los idiomas y para
                 * las limitaciones de la Licencia.
                 */

Ahora, añada las siguientes líneas de código para instanciar un TemperatureSensor y un GpsSensor como parte de la configuración de pruebas. Sólo tiene que añadir las líneas destacadas. He incluido las líneas de código existente que lo envuelven para mostrar el contexto y que usted pueda encontrarlas en el código:

                .
                .
                // crear el transportista
                var shipper = factory.newResource(NS, 'Shipper', 'shipper@email.com');
                var shipperAddress = factory.newConcept(NS, 'Address');
                shipperAddress.country = 'Panama';
                shipper.address = shipperAddress;
                shipper.accountBalance = 0;

                // crear el sensor del Temperatura
                var temperatureSensor = factory.newResource(NS, 'TemperatureSensor', 'SENSOR_TEMP001');
                // crear el sensor GPS
                var gpsSensor = factory.newResource(NS, 'GpsSensor', 'SENSOR_GPS001');

                // crear el contrato
                var contract = factory.newResource(NS, 'Contract', 'CON_001');
                contract.grower = factory.newRelationship(NS, 'Grower', 'farmer@email.com');
                contract.importer = factory.newRelationship(NS, 'Importer', 'supermarket@email.com');
                .
                .
                .then(function() {
                    return getParticipantRegistry(NS + '.Shipper');
                })
                .then(function(shipperRegistry) {
                    // añadir los transportistas
                    return shipperRegistry.addAll([shipper]);
                })
                .then(function() {
                    return getParticipantRegistry(NS + '.TemperatureSensor');
                })
                .then(function(temperatureSensorRegistry) {
                    // añadir los sensores de temperatura
                    return temperatureSensorRegistry.addAll([temperatureSensor]);
                })
                .then(function() {
                    return getParticipantRegistry(NS + '.GpsSensor');
                })
                .then(function(gpsSensorRegistry) {
                    // añadir los sensores GPS
                    return gpsSensorRegistry.addAll([gpsSensor]);
                })
                .then(function() {
                    return getAssetRegistry(NS + '.Contract');
                })
                .then(function(contractRegistry) {
                    // añadir los contratos
                    return contractRegistry.addAll([contract]);
                })
                .
                .

Ahora abra logic.js, seleccione el cuerpo completo de funciones de setupDemo, y después córtelo de logic.js y péguelo en el archivo nuevo. Finalmente, cambie el nombre de la función a instantiateModelForTesting.

Después, en logic.js, cambie el nombre de la función payOut a receiveShipment para qué coincida más claramente con la transacción del modelo. Después, añada el siguiente código para emitir el evento ShipmentReceivedEvent justo después del bloque de código que calcula el pago. Las líneas destacadas son las que se deberían modificar o añadir (modifique la línea 6, añada las líneas 17-27). He incluido las líneas de código existente que lo envuelven para mostrar el contexto.

                /**
                 * Un Importador ha recibido un envío
                 * @param {org.acme.shipping.perishable.ShipmentReceived} shipmentReceived - the ShipmentReceived transaction
                 * @transaction
                 */
                function receiveShipment(shipmentReceived) {
                    .
                    .
                    //console.log('Payout: ' + payOut);
                    contract.grower.accountBalance += payOut;
                    contract.importer.accountBalance -= payOut;

                    //console.log('Grower: ' + contract.grower.$identifier + ' new balance: ' + contract.grower.accountBalance);
                    //console.log('Importer: ' + contract.importer.$identifier + ' new balance: ' + contract.importer.accountBalance);
                
                    var NS = 'org.acme.shipping.perishable';
                    // Almacenar la transacción ShipmentReceived con el activo del Envío al que pertenece
                    shipment.shipmentReceived = shipmentReceived;

                    var factory = getFactory();
                    var shipmentReceivedEvent = factory.newEvent(NS, 'ShipmentReceivedEvent');
                    var message = 'Shipment ' + shipment.$identifier + ' received';
                    //console.log(message);
                    shipmentReceivedEvent.message = message;
                    shipmentReceivedEvent.shipment = shipment;
                    emit(shipmentReceivedEvent);
                
                    return getParticipantRegistry('org.acme.shipping.perishable.Grower')
                        .then(function (growerRegistry) {
                            // actualizar el balance del productor
                            return growerRegistry.update(contract.grower);
                        })
                    .
                    .

Ahora, es necesario añadir el chaincode JavaScript para esas tres transacciones nuevas que se han añadido al modelo, y se creará un archivo de origen JavaScript nuevo para ello. En VSCode, haga clic derecho en el directorio lib y elija Nuevo Archivo, y después introduzca shipment.js como el nombre del archivo. Añada las funciones siguientes a este archivo. Asegúrese de pegarlo en la cabecera de comentarios de Apache 2.0 de la parte superior del archivo. Vea el Listado 3.

Transacción: ShipmentPacked chaincode

Primero, añada una función para manejar la transacción ShipmentPacked. Cuando el participante Productor (o uno de sus agentes autorizados) empaqueta el envío para su recogida y transporte, invoca la transacción blockchain ShipmentPacked para registrar este hecho en el libro contable.

Cuando se invoca esta transacción, se emite ShipmentPackedEvent lo que se lo notifica a las partes interesadas, y después el chaincode actualiza el libro contable.

Dé un nombre a la función nueva packShipment utilizando la expresión de nomenclatura de funciones "verbo/objecto". En el código JavaScript, los comentarios que se muestran a continuación son obligatorios para que Hyperledger Composer reconozca la función como una transacción.

Copie todo el código siguiente y péguelo en shipment.js.

                /**
                 * ShipmentPacked transaction - se invoca cuando el Envío se empaqueta y está listo para su recogida.
                 * 
                 * @param {org.acme.shipping.perishable.ShipmentPacked} shipmentPacked - the ShipmentPacked transaction
                 * @transaction
                 */
                function packShipment(shipmentPacked) {
                    var shipment = shipmentPacked.shipment;
                    var NS = 'org.acme.shipping.perishable';
                    var contract = shipment.contract;
                    var factory = getFactory();
                
                    // Añadir la transacción ShipmentPacked en el libro contable (a través del activo Shipment)
                    shipment.shipmentPacked = shipmentPacked;

                    // Crear el mensaje
                    var message = 'Shipment packed for shipment ' + shipment.$identifier;

                    // Iniciar sesión en la consola JavaScript
                    //console.log(message);

                    // Emitir una notificación que dice a los oyentes suscritos que el envío ha sido empaquetado
                    var shipmentPackedEvent = factory.newEvent(NS, 'ShipmentPackedEvent');
                    shipmentPackedEvent.shipment = shipment;
                    shipmentPackedEvent.message = message;
                    emit(shipmentPackedEvent);

                    // Actualizar el Registro de Activos
                    return getAssetRegistry(NS + '.Shipment')
                        .then(function (shipmentRegistry) {
                            // añadir la lectura de la temperatura al envío
                            return shipmentRegistry.update(shipment);
                        });
                }

Transacción: ShipmentPickup chaincode

Después, cuando el participante Transportista (o uno de sus agentes autorizados) recoja el envío empaquetado para su transporte, invoca a la transacción blockchain ShipmentPickup para registrar este hecho en el libro contable. Añada una función para manejar la transacción ShipmentPickup llamada pickupShipment.

Cuando se invoca esta transacción, se emite ShipmentPickupEvent lo que se lo notifica a las partes interesadas, y después el chaincode actualiza el libro contable.

Copie todo el código siguiente y péguelo en shipment.js.

                /**
                 * ShipmentPickup - se invoca cuando el Envío ha sido recogido de la empaquetadora.
                 * 
                 * @param {org.acme.shipping.perishable.ShipmentPickup} shipmentPickup - the ShipmentPickup transaction
                 * @transaction
                 */
                function pickupShipment(shipmentPickup) {
                    var shipment = shipmentPickup.shipment;
                    var NS = 'org.acme.shipping.perishable';
                    var contract = shipment.contract;
                    var factory = getFactory();
                
                    // Añadir la transacción ShipmentPacked en el libro contable (a través del activo Shipment)
                    shipment.shipmentPickup = shipmentPickup;

                    // Crear el mensaje
                    var message = 'Shipment picked up for shipment ' + shipment.$identifier;

                    // Iniciar sesión en la consola JavaScript
                    //console.log(message);

                    // Emitir una notificación que dice a los oyentes suscritos que el envío ha sido empaquetado
                    var shipmentPickupEvent = factory.newEvent(NS, 'ShipmentPickupEvent');
                    shipmentPickupEvent.shipment = shipment;
                    shipmentPickupEvent.message = message;
                    emit(shipmentPickupEvent);

                    // Actualizar el Registro de Activos
                    return getAssetRegistry(NS + '.Shipment')
                        .then(function (shipmentRegistry) {
                            // añadir la lectura de la temperatura al envío
                            return shipmentRegistry.update(shipment);
                        });
                }

Transacción: ShipmentLoaded chaincode

Finalmente, cuando el participante Transportista (o uno de sus agentes autorizados) cargue el envío en portacontenedores, invoca a la transacción blockchain ShipmentLoaded para registrar este hecho en el libro contable. Añada una función para manejar el evento ShipmentLoaded llamado loadShipment.

Cuando se invoca esta transacción, se emite ShipmentLoadedEvent lo que se lo notifica a las partes interesadas, y después el chaincode actualiza el libro contable.

Copie todo el código siguiente y péguelo en shipment.js.

                /**
                 * ShipmentLoaded - se invoca cuando el Envío ha sido cargado en el buque portacontenedores.
                 * 
                 * @param {org.acme.shipping.perishable.ShipmentLoaded} shipmentLoaded - the ShipmentLoaded transaction
                 * @transaction
                 */
                function loadShipment(shipmentLoaded) {
                    var shipment = shipmentLoaded.shipment;
                    var NS = 'org.acme.shipping.perishable';
                    var contract = shipment.contract;
                    var factory = getFactory();
                
                    // Añadir la transacción ShipmentPacked en el libro contable (a través del activo Shipment)
                    shipment.shipmentLoaded = shipmentLoaded;

                    // Crear el mensaje
                    var message = 'Shipment loaded for shipment ' + shipment.$identifier;

                    // Iniciar sesión en la consola JavaScript
                    //console.log(message);

                    // Emitir una notificación que dice a los oyentes suscritos que el envío ha sido empaquetado
                    var shipmentLoadedEvent = factory.newEvent(NS, 'ShipmentLoadedEvent');
                    shipmentLoadedEvent.shipment = shipment;
                    shipmentLoadedEvent.message = message;
                    emit(shipmentLoadedEvent);

                    // Actualizar el Registro de Activos
                    return getAssetRegistry(NS + '.Shipment')
                        .then(function (shipmentRegistry) {
                            // añadir la lectura de la temperatura al envío
                            return shipmentRegistry.update(shipment);
                        });
                }
5c

Cambiar los permisos del control de acceso

En una aplicación real de blockchain, no es ideal que los participantes reciban permisos para hacer lo que ellos quieren. Por ejemplo, un productor no debería ser capaz de invocar transacciones que sólo tienen sentido para el Transportista, y el sensor GPS no debería tener permisos para registrar las lecturas de la temperatura en el blockchain.

Así que, ¿cómo se evita que ocurra esto? En otras palabras, ¿cómo debe construir su red para controlar el acceso a sus recursos? A través del archivo Lista de Control de Acceso (ACL).

Ya lo ha visto en las Partes 1 y 2 de esta serie, pero casi lo pasé por alto, y prometí cubrirlo con más detalle en un momento posterior. Bien, ha llegado el momento.

Es el momento de hablar sobre el control de accesos, que comienza con permissions.acl. Ya vimos este archivo antes, pero es el momento de utilizar ese conocimiento. Usted modificará iot-perishable-network para garantizar que sólo puedan acceder a los recursos los participantes que tiene sentido que accedan a ellos. Como ilustración, en la Tabla 1 he resumido los ajustes del control de acceso que tienen sentido para iot-perishable-network.

Es posible que piense que los nombres parecen un poco raros. Me gusta codificar el máximo de información posible en los nombres, sin hacerlos ridículamente largos o completamente sin sentido. Esto es lo que pienso: la regla se llama Participante_Operación_Recursos. Así que Grower_R_Grower significa, "Otorgar al participante Productor acceso READ (lectura) a las instancias del Productor".

Tabla 1. Control de acceso de participantes— El tipo de acceso está entre paréntesis: R = READ, U = UPDATE, C = CREATE
ParticipanteParticipantes que han accedidoActivos accedidosTransacciones accedidas
ProductorProductor (R)Envío (RU), Contrato (RU)ShipmentPacked (C)
TransportistaTransportista (R)Envío (RU), Contrato (RU)ShipmentPickup, ShipmentLoaded (C)
ImportadorImportador (R), Productor (RU)Envío (RU), ContratoShipmentReceived (C)
TemperatureSensorNONEEnvío (RU), Contrato (RU)TemperatureReading (C)
GpsSensorNONEEnvío (RU), Contrato (RU)GpsReading (C)

Las reglas están codificadas a continuación. Abra permissions.acl (en la raíz del proyecto de red), borre sus contenidos actuales y reemplácelos con los siguientes listados. El listado completo es muy largo, así que comentaré las secciones una por una.

Las primeras dos reglas otorgan permiso para que los participantes Participant y NetworkAdmin del sistema Hyperledger Composer accedan a todo lo que está en el espacio de nombres org.hyperledger.composer.system, y a todas las clases de la red, respectivamente.

Copie todo el código siguiente y péguelo en permissions.acl.

                /**
                 * Reglas de acceso del Administrador de la Red y del Sistema
                 */
                rule SystemACL {
                  description:  "System ACL to permit all access"
                  participant: "org.hyperledger.composer.system.Participant"
                  operation: ALL
                  resource: "org.hyperledger.composer.system.**"
                  action: ALLOW
                }
                rule NetworkAdminUser {
                  description: "Grant business network administrators full access to user resources"
                    participant: "org.hyperledger.composer.system.NetworkAdmin"
                    operation: ALL
                    resource: "**"
                    action: ALLOW
                }

El siguiente conjunto de reglas otorga el permiso para que los participantes de iot-perishable-network tengan acceso a otros participantes (incluidos ellos mismos). Este acceso varía ya que tiene sentido para las funciones empresariales de la red. Por ejemplo, el Productor sólo puede acceder a la clase "Grower", pero ya que el Importador ejecuta la transacción ShipmentReceived (que actualiza los balances de las cuentas del Importador y del Productor), el Importador necesita los accesos READ y UPDATE para el Importador y el Productor.

Copie todo el código siguiente y péguelo en permissions.acl.

                /**
                 * Reglas para el acceso al registro del Participante
                 */
                rule Grower_R_Grower {
                    description: "Grant Growers access to Grower resources"
                    participant: "org.acme.shipping.perishable.Grower"
                    operation: READ
                    resource: "org.acme.shipping.perishable.Grower"
                    action: ALLOW
                }
                
                rule Shipper_R_Shipper {
                    description: "Grant Shippers access to Shipper resources"
                    participant: "org.acme.shipping.perishable.Shipper"
                    operation: READ
                    resource: "org.acme.shipping.perishable.Shipper"
                    action: ALLOW
                }
                
                rule Importer_RU_Importer {
                    description: "Grant Importers access to Importer resources"
                    participant: "org.acme.shipping.perishable.Importer"
                    operation: READ,UPDATE
                    resource: "org.acme.shipping.perishable.Importer"
                    action: ALLOW
                }
                
                rule Importer_RU_Grower {
                    description: "Grant Importers access to Grower participant"
                    participant: "org.acme.shipping.perishable.Importer"
                    operation: READ,UPDATE
                    resource: "org.acme.shipping.perishable.Grower"
                    action: ALLOW
                }

Después están las reglas para acceder a los activos de la red. En este caso, quiero otorgar acceso a todos los participantes para los recursos Shipment (Envío) y Contract (Contrato).

Copie todo el código siguiente y péguelo en permissions.acl.

                /**
                 * Reglas para el acceso al registro de Activos
                 */
                rule ALL_RU_Shipment {
                    description: "Grant All Participants in org.acme.shipping.perishable namespace READ/UPDATE access to Shipment assets"
                    participant: "org.acme.shipping.perishable.*"
                    operation: READ,UPDATE
                    resource: "org.acme.shipping.perishable.Shipment"
                    action: ALLOW
                }
                
                rule ALL_RU_Contract {
                    description: "Grant All Participants in org.acme.shipping.perishable namespace READ/UPDATE access to Contract assets"
                    participant: "org.acme.shipping.perishable.*"
                    operation: READ,UPDATE
                    resource: "org.acme.shipping.perishable.Contract"
                    action: ALLOW
                }

Después están las reglas para invocar las transacciones (que requieren el acceso CREATE). Como puede ver, se realizan caso a caso porque tiene sentido. Por ejemplo, el participante Productor no necesita acceder a las transacciones del Transportista (y viceversa).

Copie todo el código siguiente y péguelo en permissions.acl.

                /**
                 * Reglas para las invocaciones de las Transacciones
                 */
                rule Grower_C_ShipmentPacked {
                    description: "Grant Growers access to invoke ShipmentPacked transaction"
                    participant: "org.acme.shipping.perishable.Grower"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.ShipmentPacked"
                    action: ALLOW
                }
                
                rule Shipper_C_ShipmentPickup {
                    description: "Grant Shippers access to invoke ShipmentPickup transaction"
                    participant: "org.acme.shipping.perishable.Shipper"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.ShipmentPickup"
                    action: ALLOW
                }
                
                rule Shipper_C_ShipmentLoaded {
                    description: "Grant Shippers access to invoke ShipmentLoaded transaction"
                    participant: "org.acme.shipping.perishable.Shipper"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.ShipmentLoaded"
                    action: ALLOW
                }
                
                rule GpsSensor_C_GpsReading {
                    description: "Grant IoT GPS Sensor devices full access to the appropriate transactions"
                    participant: "org.acme.shipping.perishable.GpsSensor"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.GpsReading"
                    action: ALLOW
                }
                
                rule TemperatureSensor_C_TemperatureReading {
                    description: "Grant IoT Temperature Sensor devices full access to the appropriate transactions"
                    participant: "org.acme.shipping.perishable.TemperatureSensor"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.TemperatureReading"
                    action: ALLOW
                }
                
                rule Importer_C_ShipmentReceived {
                    description: "Grant Importers access to invoke the ShipmentReceived transaction"
                    participant: "org.acme.shipping.perishable.Importer"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.ShipmentReceived"
                    action: ALLOW
                }

La última regla dice (en efecto), "Si hay algún recurso al que no le hemos otorgado acceso explícitamente a lo anterior, denegar su acceso". Esto es, de hecho, el comportamiento predeterminado de Hyperledger Composer, pero si eso cambia en una versión posterior, esta regla se asegura de que la red se comporte de esa manera con independencia del comportamiento predeterminado.

Copie todo el código siguiente y péguelo en permissions.acl.

                /**
                 * Se asegura de que todos los recursos estén bloqueados de forma predeterminada.
                 * Si se deben otorgar permisos a determinados recursos, esto debe ocurrir
                 * por encima de esta regla. Lo que no se especifique explícitamente se bloquea.
                 */
                rule Default {
                    description: "Deny all participants access to all resources"
                    participant: "ANY"
                    operation: ALL
                    resource: "org.acme.shipping.perishable.*"
                    action: DENY
                }

Eso es todo para permissions.acl. Guarde el archivo y prepárese para realizar las pruebas unitarias de esos permisos.

5d

Añada las pruebas de las funciones

Espere, ¿permisos de pruebas unitarias? Eso es. Todos los permisos de permissions.acl se pueden probar de forma unitaria con Cucumber. En la Parte 2 le presenté Cucumber, y ahora lo va a utilizar para realizar las pruebas unitarias de la lógica de las transacciones nuevas y de las reglas ACL que usted añadió a permissions.acl en la sección anterior.

Lo bueno de Cucumber es que su sintaxis (Gherkin) es intuitiva, así que no tengo que explicar mucho de las pruebas que usted va a ver.

Escenarios de prueba básicos

En VSCode, abra iot-perishable.feature, borre su contenido y reemplácelo con el siguiente listado a medida que examina esta sección.

Primero, usted configurará la función de pruebas como se muestra a continuación. Observe en las líneas 14-16 que la prueba emite identidades (es decir, tarjetas de ID) para los participantes especificados. Esas identidades se utilizarán más tarde en las pruebas unitarias para probar los permisos de seguridad que usted añadió a permissions.acl en la sección anterior.

Copie todo el código siguiente y péguelo en iot-perishable.feature.

                Feature: Basic Test Scenarios
                    Background:
                        Given I have deployed the business network definition ..
                        And I have added the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"supermarket@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":0}
                        ]
                        """
                        And I have added the following participants of type org.acme.shipping.perishable.TemperatureSensor
                            | deviceId |
                            | TEMP_001 |
                        And I have issued the participant org.acme.shipping.perishable.Grower#grower@email.com with the identity grower1
                        And I have issued the participant org.acme.shipping.perishable.Importer#supermarket@email.com with the identity importer1
                        And I have issued the participant org.acme.shipping.perishable.TemperatureSensor#TEMP_001 with the identity sensor_temp1
                        And I have added the following asset of type org.acme.shipping.perishable.Contract
                            | contractId | grower           | shipper               | importer           | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                            | CON_001    | grower@email.com | supermarket@email.com | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              | 
                        And I have added the following asset of type org.acme.shipping.perishable.Shipment
                            | shipmentId | type    | status     | unitCount | contract |
                            | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                        And I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 4          |
                            | SHIP_001 | 5          |
                            | SHIP_001 | 10         |
                        When I use the identity importer1

Observe la línea 28 del listado anterior. Le recomiendo que siempre ponga una "identidad predeterminada" en sus pruebas de Cucumber para asegurarse que sus permisos de seguridad siempre sean probados (vea la barra lateral). Como verá en esta sección, esto siempre se puede sobreescribir con un "When I use the identity xyz" posterior en una prueba de escenario específica, si así se prefiere.

El primer escenario que ejecutará es cuando no hay lecturas de temperatura fuera del rango acordado. Observe en la línea 2 que esta prueba se ejecutará como la identidad importer1, que es un Importador.

Copie todo el código siguiente y péguelo en iot-perishable.feature.

                    Scenario: When the temperature range is within the agreed-upon boundaries
                        When I use the identity importer1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentReceived
                            | shipment |
                            | SHIP_001 |
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":2500},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"supermarket@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":-2500}
                        ]
                        """

El siguiente escenario prueba el caso de una lectura de temperatura que está por debajo del umbral acordado. El importe del pago debería ser inferior (debido a la penalización del contrato por la baja temperatura).

Observe que la prueba sobreescribe la identidad predeterminada, y que primero se ejecuta como sensor_temp1, que es un participante de TemperatureSensor (línea 2) y después la identidad actual cambia a importer1, que es un Importador (línea 6). Si la prueba no se ejecuta de esta forma, intentará ejecutar la transacción TemperatureReading como importer1 (lo que se estableció en la sección Background), quien no tiene el permiso para invocar esa transacción. Después, la identidad se debe cambiar de nuevo o la transacción ShipmentReceived hará una llamada con error porque el participante TemperatureSensor no tiene el permiso para invocar esa transacción.

Copie todo el código siguiente y péguelo en iot-perishable.feature.

                    Scenario: When the low/min temperature threshold is breached by 2 degrees C
                        When I use the identity sensor_temp1
                        And I submit the following transaction of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 0          |
                        Then I use the identity importer1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentReceived
                            | shipment |
                            | SHIP_001 |
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":500},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"supermarket@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":-500}
                        ]
                        """

El siguiente escenario prueba el caso de una lectura de temperatura que está por encima del umbral. Al igual que en el escenario anterior, este también se debe ejecutar con dos identidades diferentes.

Copie todo el código siguiente y péguelo en iot-perishable.feature.

                    Scenario: When the hi/max temperature threshold is breached by 2 degrees C
                        When I use the identity sensor_temp1
                        And I submit the following transaction of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 12          |
                        Then I use the identity importer1
                        When I submit the following transaction of type org.acme.shipping.perishable.ShipmentReceived
                            | shipment |
                            | SHIP_001 |
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":1500},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"supermarket@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":-1500}
                        ]
                        """

Finalmente, la transacción ShipmentReceived se debe ejecutar como un Importador (el único participante que tiene el permiso para invocarla), y cuando lo haga se emitirá un evento. En este escenario la identidad se establece en la sección Background.

Copie todo el código siguiente y péguelo en iot-perishable.feature.

                    Scenario: When shipment is received a ShipmentReceivedEvent should be broadcast
                        When I submit the following transaction of type org.acme.shipping.perishable.ShipmentReceived
                            | shipment |
                            | SHIP_001 |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentReceivedEvent
                            | message                    | shipment |
                            | Shipment SHIP_001 received | SHIP_001 |

Eso es todo para iot-perishable.feature. Continúe y guarde el archivo.

Pruebas para los dispositivos de IoT

Las pruebas de las funciones restantes funcionan esencialmente igual que las que acaba de ver, así que no le aburriré con una explicación repetida. Según vaya completando esta sección, siga las direcciones y peque los listados completos en los nuevos archivos .feature.

En VSCode, haga clic en el directorio features, elija Nuevo Archivo y llame al archivo sensors.feature. Cuando el archivo se abra en la ventana del editor, copie y pegue el siguiente listado en un archivo vacío nuevo:

Copie todo el código siguiente y péguelo en sensors.feature.

                Feature: Tests related to IoT Devices
                
                    Background:
                        Given I have deployed the business network definition ..
                        And I have added the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Shipper", "email":"shipper@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"Panama"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"importer@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":0}
                        ]
                        """
                        And I have added the following participants of type org.acme.shipping.perishable.TemperatureSensor
                            | deviceId |
                            | TEMP_001 |
                        And I have added the following participant of type org.acme.shipping.perishable.GpsSensor
                            | deviceId |
                            | GPS_001  |
                        And I have issued the participant org.acme.shipping.perishable.Grower#grower@email.com with the identity grower1
                        And I have issued the participant org.acme.shipping.perishable.Shipper#shipper@email.com with the identity shipper1
                        And I have issued the participant org.acme.shipping.perishable.TemperatureSensor#TEMP_001 with the identity sensor_temp1
                        And I have issued the participant org.acme.shipping.perishable.GpsSensor#GPS_001 with the identity sensor_gps1
                        And I have added the following asset of type org.acme.shipping.perishable.Contract
                            | contractId | grower           | shipper               | importer              | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                            | CON_001    | grower@email.com | shipper@email.com     | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              | 
                        And I have added the following asset of type org.acme.shipping.perishable.Shipment
                            | shipmentId | type    | status     | unitCount | contract |
                            | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                
                    Scenario: Test TemperatureThresholdEvent is emitted when the max temperature threshold is violated
                        When I use the identity sensor_temp1
                        When I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 11         |
                        
                        Then I should have received the following event of type org.acme.shipping.perishable.TemperatureThresholdEvent
                            | message                                                                          | temperature | shipment |
                            | Temperature threshold violated! Emitting TemperatureEvent for shipment: SHIP_001 | 11          | SHIP_001 |    
                
                    Scenario: Test TemperatureThresholdEvent is emitted when the min temperature threshold is violated
                        When I use the identity sensor_temp1
                        When I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 0          |
                        
                        Then I should have received the following event of type org.acme.shipping.perishable.TemperatureThresholdEvent
                            | message                                                                          | temperature | shipment |
                            | Temperature threshold violated! Emitting TemperatureEvent for shipment: SHIP_001 | 0           | SHIP_001 |


                Scenario: Test ShipmentInPortEvent is emitted when GpsReading indicates arrival at destination port
                        When I use the identity sensor_gps1
                        When I submit the following transaction of type org.acme.shipping.perishable.GpsReading
                            | shipment | readingTime | readingDate | latitude | latitudeDir | longitude | longitudeDir |
                            | SHIP_001 | 120000      | 20171025    | 40.6840  | N           | 74.0062   | W            |
                
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentInPortEvent
                        | message                                                                           | shipment |
                        | Shipment has reached the destination port of /LAT:40.6840N/LONG:74.0062W | SHIP_001 |
            Scenario: GpsSensor sensor_gps1 can invoke GpsReading transaction
                        When I use the identity sensor_gps1
                        When I submit the following transaction of type org.acme.shipping.perishable.GpsReading
                            | shipment | readingTime | readingDate | latitude | latitudeDir | longitude | longitudeDir |
                            | SHIP_001 | 120000      | 20171025    | 40.6840  | N           | 74.0062   | W            |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentInPortEvent
                            | message                                                                  | shipment |
                            | Shipment has reached the destination port of /LAT:40.6840N/LONG:74.0062W | SHIP_001 |

                    Scenario: Temperature Sensor cannot invoke GpsReading transaction
                        When I use the identity sensor_temp1
                        When I submit the following transaction of type org.acme.shipping.perishable.GpsReading
                            | shipment | readingTime | readingDate | latitude | latitudeDir | longitude | longitudeDir |
                            | SHIP_001 | 120000      | 20171025    | 40.6840  | N           | 74.0062   | W            |
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/

                    Scenario: Gps Sensor cannot invoke TemperatureReading transaction
                        When I use the identity sensor_gps1
                        When I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 11         |        
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/
                
                    Scenario: Grower cannot invoke TemperatureReading transaction
                        When I use the identity sensor_gps1
                        When I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 11         |        
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/

Ahora, guarde este archivo.

Pruebas relacionadas con los Productores

En VSCode, haga clic en el directorio features, elija Nuevo Archivo y llame al archivo grower.feature. Cuando el archivo se abra en la ventana del editor, copie y pegue el siguiente listado en un archivo vacío nuevo:

Copie todo el código siguiente y péguelo en grower.feature.

                Feature: Tests related to Growers
                
                    Background:
                        Given I have deployed the business network definition ..
                        And I have added the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Shipper", "email":"shipper@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"Paname"}, "accountBalance":0}
                        ]
                        """
                        And I have issued the participant org.acme.shipping.perishable.Grower#grower@email.com with the identity grower1
                        And I have issued the participant org.acme.shipping.perishable.Shipper#shipper@email.com with the identity shipper1
                        And I have added the following asset of type org.acme.shipping.perishable.Contract
                            | contractId | grower           | shipper               | importer              | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                            | CON_001    | grower@email.com | shipper@email.com     | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              | 
                        And I have added the following asset of type org.acme.shipping.perishable.Shipment
                            | shipmentId | type    | status     | unitCount | contract |
                            | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                        When I use the identity grower1
                
                    Scenario: grower1 can read Grower assets
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0}
                        ]
                        """
                    
                    Scenario: grower1 invokes the ShipmentPacked transaction
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentPacked
                            | shipment |
                            | SHIP_001 |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentPackedEvent
                            | message                               | shipment |
                            | Shipment packed for shipment SHIP_001 | SHIP_001 |
                
                    Scenario: shipper1 cannot read Grower assets
                        When I use the identity shipper1
                        And I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0}
                        ]
                        """
                        Then I should get an error matching /Object with ID .* does not exist/
                    
                    Scenario: shipper1 cannot invoke the ShipmentPacked transaction
                        When I use the identity shipper1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentPacked
                            | shipment |
                            | SHIP_001 |
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/

Ahora, guarde este archivo. Observe la identidad predeterminada que se utiliza (línea destacada). Sólo se necesita establecer una identidad para un escenario específico en el caso en el que se necesite una identidad diferente.

En VSCode, haga clic en el directorio features, elija Nuevo Archivo y llame al archivo shipper.feature. Cuando el archivo se abra en la ventana del editor, copie y pegue el siguiente listado en un archivo vacío nuevo:

Copie todo el código siguiente y péguelo en shipper.feature.

                Feature: Tests related to Shippers
                
                    Background:
                        Given I have deployed the business network definition ..
                        And I have added the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Shipper", "email":"shipper@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"Paname"}, "accountBalance":0}
                        ]
                        """
                        And I have issued the participant org.acme.shipping.perishable.Grower#grower@email.com with the identity grower1
                        And I have issued the participant org.acme.shipping.perishable.Shipper#shipper@email.com with the identity shipper1
                        And I have added the following asset of type org.acme.shipping.perishable.Contract
                            | contractId | grower           | shipper               | importer              | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                            | CON_001    | grower@email.com | shipper@email.com     | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              |
                        And I have added the following asset of type org.acme.shipping.perishable.Shipment
                            | shipmentId | type    | status     | unitCount | contract |
                            | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                        When I use the identity shipper1

                    Scenario: shipper1 can read Shipper assets
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Shipper", "email":"shipper@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"Paname"}, "accountBalance":0}
                        ]
                        """
                    
                    Scenario: shipper1 invokes the ShipmentPickup transaction
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentPickup
                            | shipment |
                            | SHIP_001 |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentPickupEvent
                            | message                                  | shipment |
                            | Shipment picked up for shipment SHIP_001 | SHIP_001 |
                
                    Scenario: shipper1 invokes the ShipmentLoaded transaction
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentLoaded
                            | shipment |
                            | SHIP_001 |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentLoadedEvent
                            | message                               | shipment |
                            | Shipment loaded for shipment SHIP_001 | SHIP_001 |
                
                    Scenario: grower1 cannot invoke the ShipmentPickup transaction
                        When I use the identity grower1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentPickup
                            | shipment |
                            | SHIP_001 |
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/
                
                    Scenario: grower1 cannot invoke the ShipmentPickup transaction
                        When I use the identity grower1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentLoaded
                            | shipment |
                            | SHIP_001 |
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/

Ahora, guarde este archivo.

Eso es todo para las pruebas de las funciones de Cucumber.

5e

Construya y realice las pruebas unitarias de la red

Ahora es el momento de que construya y pruebe unitariamente su red. Vaya a la línea de comando (Ubuntu) o abra una ventana del terminal (MacOS), e introduzca este comando

npm install && npm test

Usted ha ejecutado esos comandos antes en la Parte 2 de esta serie de tutoriales. Verá muchos resultados, pero cuando se hayan ejecutado las pruebas unitarias, debería ver esto:

                  ✔ And I have issued the participant org.acme.shipping.perishable.Shipper#shipper@email.com with the identity shipper1
                  ✔ And I have added the following asset of type org.acme.shipping.perishable.Contract
                      | contractId | grower           | shipper           | importer              | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                      | CON_001    | grower@email.com | shipper@email.com | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              |
                  ✔ And I have added the following asset of type org.acme.shipping.perishable.Shipment
                      | shipmentId | type    | status     | unitCount | contract |
                      | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                  ✔ When I use the identity shipper1
                  ✔ When I use the identity grower1
                  ✔ And I submit the following transaction of type org.acme.shipping.perishable.ShipmentLoaded
                      | shipment |
                      | SHIP_001 |
                  ✔ Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/
                
                20 scenarios (20 passed)
                229 steps (229 passed)
                0m09.497s

Las líneas 15-16 anteriores muestran los 20 escenarios (formados por 229 pasos) en caso de que se ejecuten todos sin errores. ¡Ahora está listo para desplegar la red!

6

Despliegue sus cambios en la red

Si ha estado siguiendo el tutorial, ya ha desplegado iot-perishable-network en Hyperledger Fabric local y hay instanciado del modelo. Desafortunadamente, es necesario añadir los participantes TemperatureSensor y GpsSensor en el registro de participantes. Esto se puede hacer a través del CLI, pero quiero mostrarle cómo se desmantela y se limpia el Hyperledger Fabric local, que es algo que hay que hacer de vez en cuando.

Cuando desmantele Hyperledger Fabric con el procedimiento que describo a continuación, este eliminará la red y los materiales criptográficos que haya generado hasta el momento. Por razones obvias es una cosa que no se quiere hacer en producción, pero esto es desarrollo, así que es algo que necesita saber hacer.

Navegue a su directorio $COMPOSER_ROOT e introduzca el comando ./teardownFabric.sh. Verá un resultado como este:

                $ ./teardownFabric.sh 
                Development only script for Hyperledger Fabric control
                Running 'teardownFabric.sh'
                .
                .                
                # Shut down the Docker containers for the system tests.
                cd "${DIR}"/composer
                ARCH=$ARCH docker-compose -f docker-compose.yml kill && docker-compose -f docker-compose.yml down
                Killing peer0.org1.example.com ... done
                Killing ca.org1.example.com    ... done
                Killing orderer.example.com    ... done
                Killing couchdb                ... done
                WARNING: The ARCH variable is not set. Defaulting to a blank string.
                Removing peer0.org1.example.com ... done
                Removing ca.org1.example.com    ... done
                Removing orderer.example.com    ... done
                Removing couchdb                ... done
                Removing network composer_default
                .
                .                
                # Your system is now clean

Ahora, limpie el directorio ~/.composer, lo que borrará todas las tarjetas y los materiales criptográficos que ha generado hasta el momento. Después, vuelva a ejecutar el script createPeerAdmin.sh:

                $ rm -Rf ~/.composer
                $ ./createPeerAdminCard.sh 
                Development only script for Hyperledger Fabric control
                Running 'createPeerAdminCard.sh'
                FABRIC_VERSION is unset, assuming hlfv1
                FABRIC_START_TIMEOUT is unset, assuming 15 (seconds)
                
                Using composer-cli at v0.15.0
                Successfully created business network card to /tmp/PeerAdmin@hlfv1.card

                Command succeeded

                Successfully imported business network card: PeerAdmin@hlfv1

                Command succeeded

                Hyperledger Composer PeerAdmin card has been imported
                The following Business Network Cards are available:


                ┌─────────────────┬───────────┬─────────┐
                │ CardName        │ UserId    │ Network │
                ├─────────────────┼───────────┼─────────┤
                │ PeerAdmin@hlfv1 │ PeerAdmin │         │
                └─────────────────┴───────────┴─────────┘

                Issue composer card list --name <CardName>  to get details of the card

                Command succeeded

Después, inicie el Hyperledger Fabric local ejecutando el script startFabric.sh, después navegue hasta el directorio iot-perishable-network, despliegue en red y vuelva a importar la tarjeta admin@iot-perishable-network:

                $ cd $COMPOSER_ROOT/fabric-tools
                $./startFabric.sh
                .
                .
                $ composer network deploy -a dist/iot-perishable-network.bna -A admin -S adminpw -c PeerAdmin@hlfv1 -f networkadmin.card
                Deploying business network from archive: dist/iot-perishable-network.bna
                Business network definition:
                	Identifier: iot-perishable-network@0.1.12
                	Description: Shipping Perishable Goods Business Network
                
                ✔ Deploying business network definition. This may take a minute...
                Successfully created business network card to networkadmin.card

                Command succeeded
                                $ composer card import --file networkadmin.card
                Successfully imported business network card: admin@iot-perishable-network

                Command succeeded

Finalmente, es necesario ejecutar la transacción SetupDemo para instanciar el modelo, ¡o no habrá participantes a los que emitir tarjetas de ID!

                $ composer transaction submit --card admin@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.SetupDemo"}'
                Transaction Submitted.

                Command succeeded

Desde ahora, si usted realiza cambios en la red (mientras no impliquen modificaciones en ninguna de las entradas del blockchain), sólo tendrá que actualizarla:

                $ composer network update -a dist/iot-perishable-network.bna --card admin@iot-perishable-network
                Deploying business network from archive: dist/iot-perishable-network.bna
                Business network definition:
                    Identifier: iot-perishable-network@0.1.12
                    Description: Shipping Perishable Goods Business Network
                
                ✔ Updating business network definition. This may take a few seconds...
                Successfully created business network card to undefined

                Command succeeded
6a

Emitir IDs

Ahora es el momento de emitir algunos IDs. Usted ya lo realizó antes en Playground. Ahora utilizará el CLI para emitir tarjetas de ID para los participantes que se muestran en la Tabla 2.

Tabla 2. Identidades a emitir de los participantes
ParticipanteIdentidadNombre del archivo de la tarjeta del ID
Productorgrower1grower1.card
Transportistashipper1shipper1.card
Importadorimporter1importer1.card
TemperatureSensorsensor_temp1sensor_temp1.card
GpsSensorsensor_gps1sensor_gps1.card

En la sección CLI usted invocó transacción SetupDemo, que instanció la red. Recuerde, no se puede emitir una tarjeta de ID para un participante que no esté en el Registro de Participantes.

Para emitir una tarjeta de ID para un participante, antes tiene que ejecutar el comando composer identity issue especificando el archivo de la tarjeta, y luego importando la tarjeta a la cartera local. Utilice la tarjeta admin@iot-perishable-network para un autenticar cuando ejecute el comando composer identity issue.

El formato general para el comando para iot-perishable-network es este:

                composer identity issue --card admin@iot-perishable-network --file ID_CARD_FILE --newUserId IDENTITY --participantId 'resource:org.acme.shipping.perishable.PARTICIPANT#PARTICIPANT_ID'

Donde:

ID_CARD_FILE— es el archivo donde se almacenará la tarjeta de ID (vea la Tabla 2).

IDENTITY— es la identidad que se va a emitir (vea la Tabla 2).

PARTICIPANT_CLASS— es la clase de participante (por ejemplo, Productor).

PARTICIPANT_ID— es el ID del participante cuando se instanció en el registro (por ejemplo, farmer@email.com).

Tarjeta de ID: Productor

                $ composer identity issue --card admin@iot-perishable-network --file grower1.card --newUserId grower1 --participantId 'resource:org.acme.shipping.perishable.Grower#farmer@email.com'
                
                Command succeeded

                $ composer card import --file grower1.card 
                Successfully imported business network card: grower1@iot-perishable-network
                
                Command succeeded

Tarjeta de ID: Transportista

                $ composer identity issue --card admin@iot-perishable-network --file shipper1.card --newUserId shipper1 --participantId 'resource:org.acme.shipping.perishable.Shipper#shipper@email.com'
                
                Command succeeded
                
                Ix:~/HyperledgerComposer/developerWorks/iot-perishable-network sperry$ composer card import --file shipper1.card 
                Successfully imported business network card: shipper1@iot-perishable-network
                
                Command succeeded

Tarjeta de ID: Importador

                $ composer identity issue --card admin@iot-perishable-network --file importer1.card --newUserId importer1 --participantId 'resource:org.acme.shipping.perishable.Importer#supermarket@email.com'
                
                Command succeeded
                Ix:~/HyperledgerComposer/developerWorks/iot-perishable-network sperry$ composer card import --file importer1.card 
                Successfully imported business network card: importer1@iot-perishable-network
                
                Command succeeded

Tarjeta de ID: TemperatureSensor

                $ composer identity issue --card admin@iot-perishable-network --file sensor_temp1.card --newUserId sensor_temp1 --participantId 'resource:org.acme.shipping.perishable.TemperatureSensor#SENSOR_TEMP001'
                
                Command succeeded

                $ composer card import --file sensor_temp1.card
                Successfully imported business network card: sensor_temp1@iot-perishable-network

                Command succeeded

Tarjeta de ID: GpsSensor

                $ composer identity issue --card admin@iot-perishable-network --file sensor_gps1.card --newUserId sensor_gps1 --participantId 'resource:org.acme.shipping.perishable.GpsSensor#SENSOR_GPS001'
                
                Command succeeded
                
                Ix:~/HyperledgerComposer/developerWorks/iot-perishable-network sperry$ composer card import --file sensor_gps1.card 
                Successfully imported business network card: sensor_gps1@iot-perishable-network
                
                Command succeeded

Ahora que ha emitido las tarjetas de ID para todos los participantes de la red, y que ha importado esas tarjetas a la cartera de su Hyperledger Fabric local, puede utilizar el CLI para simular el flujo de trabajo de la red empresarial IoT de Alimentos Duraderos desde la línea de comando.

Para ver las tarjetas que ha emitido, ejecute el comando composer card list comando:

                $ composer card list
                The following Business Network Cards are available:
                
                
                ┌─────────────────────────────────────┬──────────────┬────────────────────────┐
                │ CardName                            │ UserId       │ Network                │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ admin@iot-perishable-network        │ admin        │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ importer1@iot-perishable-network    │ importer1    │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ grower1@iot-perishable-network      │ grower1      │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ sensor_temp1@iot-perishable-network │ sensor_temp1 │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ sensor_gps1@iot-perishable-network  │ sensor_gps1  │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ shipper1@iot-perishable-network     │ shipper1     │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ PeerAdmin@hlfv1                     │ PeerAdmin    │                        │
                └─────────────────────────────────────┴──────────────┴────────────────────────┘
                
                Issue composer card list --name <CardName>  to get details of the card
                
                Command succeeded
6b

Enviar transacciones

El formato general del comando composer transaction submit es:

                composer transaction submit --card CARD_NAME -d 'DATA'

Donde:

CARD_NAME— es el nombre de la tarjeta que se va a utilizar (vea la Tabla 2).

DATA es un objeto JSON que contiene los datos de la transacción. Se puede copiar el formato de los datos de cualquier transacción desde Playground, eliminar las líneas nuevas y reemplazar los valores específicos de los datos (Sí, así es como obtuve los datos de la transacción que verá en esta sección).

Enviar transacciones

En esta sección, usted enviará las siguientes transacciones a través del CLI:

  • ShipmentPacked
  • ShipmentPickup
  • ShipmentLoaded
  • TemperatureReading
  • GpsReading
  • ShipmentReceived

Cada una de esas transacciones corresponde a un paso del flujo de trabajo de mover productos perecederos desde el Productor hasta el Importador.

Envie una transacción ShipmentPacked, utilizando la tarjeta de ID grower1 para autenticar y autorizar con la red:

                $ composer transaction submit --card grower1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentPacked", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.

                Command succeeded

Esta transacción ha tenido éxito porque el participante Productor tiene el permiso para ejecutar la transacción ShipmentPacked. Intente ejecutar la transacción utilizando la tarjeta de ID shipper1 sólo por diversión:

                $ composer transaction submit --card shipper1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentPacked", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Error: Error trying invoke business network. Error: chaincode error (status: 500, message: Error: Participant 'org.acme.shipping.perishable.Shipper#shipper@email.com' does not have 'CREATE' access to resource 'org.acme.shipping.perishable.ShipmentPacked#8db39906c73c0821021489834f9e5fb37f29bab4253840cb756038b77da4dc00')
                Command failed

El participante Transportista no tiene acceso a la transacción ShipmentPacked, así que el intento falla, lo que muestra que los permisos que usted modificó antes en el tutorial están funcionando tal como se esperaba (por supuesto que usted lo sabía cuando ejecutó la prueba unitaria, pero siempre está bien verlo en acción).

El segundo paso del flujo de trabajo de enviar los bienes perecederos desde el Productor hasta el Importador es la recogida del envío empaquetado, de lo que se encarga el Transportista:

                $ composer transaction submit --card shipper1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentPickup", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Una vez que el envío haya sido recogido, el Transportista lo carga en un buque portacontenedores y ejecuta la transacción ShipmentLoaded para registrarlo en el libro contable:

                $ composer transaction submit --card shipper1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentLoaded", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Por el camino, el participante TemperatureSensor está tomando las lecturas de dentro del contenedor del carguero las está registrando en el libro contable. Simule alguna de ellas de esta manera:

                $ composer transaction submit --card sensor_temp1@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 2, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded
                $ composer transaction submit --card sensor_temp1@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 3, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded
                
                Ix:~/HyperledgerComposer/developerWorks/iot-perishable-network sperry$ composer transaction submit --card sensor_temp1@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 11, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Eso fueron tres transacciones de TemperatureReading, la última estaba a 11° C, es un grado por encima del umbral del contrato, así que la penalización por la alta temperatura entrará en efecto cuando se reciba el envío.

Ahora, envíe una transacción de GPS que indique que el buque portacontenedores ha llegado a su destino, que es el Puerto de Nueva York/Nueva Jersey:

                $ composer transaction submit --card sensor_gps1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.GpsReading", "readingTime": "2200", "readingDate": "20171118", "latitude": "40.6840", "latitudeDir": "N", "longitude": "74.0062", "longitudeDir": "W", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.

                Command succeeded

El paso final del flujo de trabajo es para que el Importador reciba el envío e invoque la transacción ShipmentReceived para registrarlo en el blockchain:

                $ composer transaction submit --card importer1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentReceived", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Eso no parece muy emocionante, ¿verdad? Continúe y lance el servidor REST:

                $ composer-rest-server
                ? Enter the name of the business network card to use: admin@iot-perishable-network
                ? Specify if you want namespaces in the generated REST API: always use namespaces
                ? Specify if you want to enable authentication for the REST API using Passport: No
                ? Specify if you want to enable event publication over WebSockets: No
                ? Specify if you want to enable TLS security for the REST API: No
                Discovered types from business network definition
                Generating schemas for all types in business network definition ...
                Generated schemas for all types in business network definition
                Adding schemas for all types to Loopback ...
                Added schemas for all types to Loopback
                Web server listening at: http://localhost:3000
                Browse your REST API at http://localhost:3000/explorer
                To restart the REST server using the same options, issue the following command:
                   composer-rest-server -c admin@iot-perishable-network -n always

                Discovering types from business network definition ...

Ahora apunte su navegador a localhost:3000, ubique el activo "Shipment" y ejecute el método /Get. Usted verá todas las transacciones que ha emitido, registradas en el blockchain como parte del activo "Shipment". La Imagen 5 muestra lo que usted debería ver en el cuerpo de la respuesta de la solicitud /Get.

Figura 5. El activo Envío mostrando las transacciones que se han introducido a través del CLI
Shipment asset showing transactions entered                     through the CLI
Shipment asset showing transactions entered through the CLI

Hay bastantes datos, así que sólo he podido mostrar unos pocos en la Imagen 5, pero continúe, lance el servidor REST por usted mismo y deslícese a través del activo Envío y vea los resultados por usted mismo.

Video: Demo de Cierre

Toda la información de la sección anterior y de Playground (donde corresponda), se recoge en este video:

Conclusión de la Parte 3

Este tutorial ha cubierto mucho terreno. Primero, usted instaló, inició y después desplegó iot-perishable-network en una instancia local de Hyperledger Fabric.

Después, instaló y ejecutó la interfaz REST, que puede utilizar para acceder a la red. Espero que tenga la oportunidad de ver el video, donde hago un repaso más amplio. Si es así, asegúrese y verifíquelo.

Después de eso, vio cómo funciona el control de acceso en Hyperledger Composer, lo que incluye las reglas ACL y dónde residen en el código fuente de la red.

Después trabajó con Composer Command Line Interface (CLI) para hacer un ping a la red que se ejecutaba, ejecutó SetupDemo y otras transacciones y actualizó la red según hacía cambios en desarrollo.

Finalmente, el golpe de gracia: usted modificó iot-perishable-network para transformarlo en una aplicación de blockchain del mundo real, escriba pruebas de funciones de Cucumber, emita IDs para todos los participantes y ejecute todas las transacciones a través del CLI.

En ese punto, usted debería tener todo lo que necesitaba para empezar a desarrollar sus propias aplicaciones de blockchain con Hyperledger Composer. ¡Buena suerte!


Recursos para Descargar


Temas relacionados


Comentarios

Inicie Sesión o Regístrese para agregar comentarios.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=Internet of Things, Cloud computing
ArticleID=1057969
ArticleTitle=Aspectos básicos de Hyperledger Composer, Parte 3: Despliegue de forma local, interactúe y extienda su red blockchain
publish-date=02082018