Conteúdo


Fundamentos básicos do Hyperledger Composer, Parte 3

Implemente sua rede de blockchain localmente, interaja com ela e amplie-a

Explorando mais ferramentas, UIs e recursos de segurança do Hyperledger Composer

Comments

Conteúdos da série:

Esse conteúdo é a parte # de # na série: Fundamentos básicos do Hyperledger Composer, Parte 3

Fique ligado em conteúdos adicionais dessa série.

Esse conteúdo é parte da série:Fundamentos básicos do Hyperledger Composer, Parte 3

Fique ligado em conteúdos adicionais dessa série.

Este tutorial é uma continuação da Parte 1, na qual você aprendeu como modelar e testar uma rede de negócios simples em uma versão local do Hyperledger Composer Playground. Em A Parte 2, você viu como modelar um sensor de GPS de IoT em um contêiner de envio incluindo leituras do GPS no ativo de envio e como modificar o chaincode para enviar um alerta quando o envio atinge sua porta de destino. Em seguida, você implementou a rede Mercadorias Perecíveis de amostra na IBM Cloud no playground on-line do Composer.

Agora, nesta terceira e última parte, você instalará o Hyperledger Fabric no computador e implementará o business network archive (BNA) em uma instância do Hyperledger Fabric em execução na sua máquina (mencionada como Hyperledger Fabric local). Você instalará mais ferramentas e irá gerar uma interface REST baseada em loopback que poderá ser usada para interagir com o aplicativo de blockchain da rede de amostra.

A Parte 3 também inclui uma discussão detalhada sobre os conceitos de segurança do Hyperledger Composer. Esta parte é finalizada com etapas para juntar tudo isso e ampliar a iot-perishable-network para criar uma versão mais "real" da rede Mercadorias Perecíveis.

Pré-requisitos

Pressupõe-se que você tenha realizado a A Parte 2 e que você tenha os seguintes softwares instalados em seu computador:

1

Implemente a rede no Hyperledger Fabric local

O Hyperledger Fabric é uma estrutura para desenvolver aplicativos de blockchain para propósitos comerciais e, como você já aprendeu, o Hyperledger Composer é uma ferramenta relacionada que cria aplicativos de blockchain que são executados no Hyperledger Fabric com mais facilidade. Até agora, você usou o playground do Composer no modo "somente navegador" (na Parte 1) e o playground on-line do Composer na IBM Cloud (Parte 2).

Agora, você vai instalar e executar o Hyperledger Fabric no seu computador e vai usar a interface da linha de comandos (CLI) do Composer para interagir com ele.

1a

Obtenha o Hyperledger Fabric

Primeiro, crie um diretório no computador no qual você deseja realizar o desenvolvimento do Hyperledger Composer local. Neste tutorial, vou me referir a esse local como $COMPOSER_ROOT no código e vou me referir a ele no contexto como diretório raiz do Composer. Este diretório pode estar em qualquer local desejado, mas eu recomendo que ele seja criado fora da sua pasta inicial e que a variável de ambiente COMPOSER_ROOT seja configurada para esse diretório no shell atual, pois é assim que vou me referir a esse local ao longo deste tutorial.

Agora, faça o download do arquivo de distribuição fabric-dev-servers.zip para o diretório raiz do Composer usando o comando curl e descompacte-o. A sequência de comandos é semelhante a esta (considerando ~/HyperledgerComposer como o diretório raiz neste exemplo):

                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 o Hyperledger Fabric

Observação: antes de continuar, assegure-se de que o Docker esteja em execução.

Na primeira vez que você executar o Hyperledger Fabric, execute esta sequência de comandos:

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

Demorará alguns minutos para que o primeiro comando seja executado, puxando todas as imagens do Docker necessárias. O segundo comando inicia o Hyperledger Fabric local. O último comando emite um cartão de ID para o administrador da malha, que é o PeerAdmin. Não se preocupe com isso agora, vou falar sobre os cartões de ID mais adiante no tutorial. A saída do script createPeerAdminCard.sh é semelhante a esta:

                $ ./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

Anote o nome do cartão porque ele será necessário para executar todos os comandos da CLI neste tutorial.

Basta executar o script createPeerAdminCard.sh uma única vez. A partir de agora, sempre que você iniciar o Hyperledger Fabric, bastará executar o script startFabric.sh desta forma:

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

Quando o script for concluído, o Hyperledger Fabric estará em execução no computador. Para encerrar o Hyperledger Fabric local, execute o stopFabric.sh .

Para cumprir com os propósitos deste tutorial, eu sugiro deixar o Hyperledger Fabric ativo agora.

1c

Implemente no Hyperledger Fabric

Na Parte 2, você clonou o projeto developerWorks do GitHub e modificou a rede Mercadorias Perecíveis. Eu forneci uma versão concluída dessa rede na developerWorks/iot-perishable-network . Vou usar essa rede no restante deste tutorial.

Para implementar o iot-perishable-network no Fabric local, use a CLI do Composer e execute esta sequência 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

O comando composer network deploy implementa o archive de rede especificado (dist/iot-perishable-network.bna) no Hyperledger Fabric local, usando o cartão PeerAdmin que você já criou quando executou o script createPeerAdminCard.sh para autenticação. Quando a implementação for concluída, um cartão de ID será emitido para o administrador da rede, cujas credenciais, — ou seja, o ID do usuário e a senha (ou o segredo), — serão armazenadas no networkadmin.card .

A saída será semelhante a esta:

                $ 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

O comando composer card import informa o Hyperledger Composer que ele deve importar o arquivo de cartão especificado que será usado mais tarde para autenticar o usuário cujas credenciais estão armazenadas no cartão. Nesse caso, o cartão é para o administrador da rede. Vou falar mais sobre os cartões posteriormente. A saída será semelhante a esta:

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

Interaja com o blockchain por meio da interface REST do Composer

Você implementou o iot-perishable-network no Hyperledger Fabric local, mas como é possível verificar o que está lá? Como interagir com ele? O Hyperledger Composer fornece uma ferramenta chamada composer-rest-server que gera uma interface REST baseada em loopback para acessar a rede.

2a

Instale o gerador de interface REST do Composer

Primeiro, é necessário instalar o gerador de loopback composer-rest-server. Acesse uma linha de comandos (Ubuntu) ou abra uma janela do terminal (MacOS) e insira este comando:

npm install -g composer-rest-server

Quando a instalação for concluída, verifique se composer-rest-server foi instalado corretamente executando composer-rest-server -v:

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

Gere a interface REST

Assegure-se de que o Hyperledger Fabric esteja em execução e que a iot-perishable-network seja implementada antes de gerar a interface REST (ou não haverá nada para ser conectado). Na linha de comando, execute composer-rest-server . Esse comando solicitará que você insira informações. Será exibido algo como isto:

Lista 1. Iniciando o 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

O servidor REST precisa saber como se autenticar com o Hyperledger Fabric para que possa se comunicar com a rede de negócios. Forneça um cartão de ID para isso (linha 2) como o cartão de ID admin@iot-perishable-network que já foi mostrado neste tutorial.

Os namespaces ajudam a evitar colisões de nome. No campo iot-perishable-network, isso não tem muita importância, pois se trata apenas de alguns ativos, participantes e transações. Na minha opinião, é sempre bom usar namespaces (linha 3). Testar redes reais com muitos participantes, ativos e transações já é difícil o suficiente e o uso de namespaces ajudará a evitar problemas relacionados a nomes aglutinados.

Ao usar a interface REST para os testes, não se preocupe com autenticação (linha 4), eventos e WebSockets (linha 5) ou Segurança TLS (linha 6) por enquanto. No entanto, se você decidir implementar o servidor REST como parte da solução de blockchain de produção, assegure-se de verificar os links aqui ou nos Tópicos relacionados no final deste tutorial.

Na próxima vez que desejar iniciar o servidor REST com as mesmas opções, bastará usar o comando mostrado (linha 9) e ignorar a entrevista!

2c

Use a interface REST

Para usar a interface REST, abra um navegador e aponte-o para o endereço mostrado na linha 18 em Listagem 1. Será exibido algo como isto:

Figura 1. A interface REST
The REST interface
The REST interface

A interface é bastante intuitiva. Para trabalhar com um objeto, clique nele e, quando ele for expandido, serão exibidos os métodos REST que você poderá chamar (por exemplo, /GET, /POSTe assim por diante). Para chamar a transação SetupDemo (que instancia o modelo de negócio), clique na linha que contém SetupDemo, que, em seguida, será expandido para o POST . Clique no botão Experimente! para chamar a transação. Caso ela seja bem-sucedida, será exibido um código de resposta HTTP 200. Em seguida, será possível navegar no modelo e observar os diversos objetos, como o Contrato, mostrado na Figura 2.

Figura 2. A interface REST mostrando o objeto Contrato
The REST interface showing the Contract object
The REST interface showing the Contract object

No campo vídeo Vou mostrar como usar a interface REST em detalhes, portanto, não deixe de conferir.

2d

Proteja a interface REST

Isso não faz parte do escopo deste tutorial, mas se você planejar executar a interface REST em produção, será necessário usar uma estratégia para lidar com isso. Um tutorial inteiro poderia ser criado somente para falar desse assunto! Felizmente, há recursos no website do Hyperledger Composer para ajudar. Aqui estão alguns links para os documentos do Composer:

Para facilitar, esses links também estão listados nos Tópicos relacionados no final deste tutorial.

Vídeo: Usando a interface REST do Composer

3

Configure a segurança do Hyperledger Composer

Existem dois níveis de segurança com o Hyperledger Composer:

  • Administrador do Hyperledger Fabric
  • Administrador da rede de negócios

O administrador do Hyperledger Fabric local é o Administrador Peer (ou PeerAdmin, abreviando), que você criou quando instalou o Hyperledger Fabric local. Toda rede de negócios também precisa ter um administrador, que é criado quando a rede é implementada pelo administrador do Hyperledger Fabric. A autenticação de ambos é manipulada usando Cartões de ID, que serão abordados em seguida.

3a

Cartões de ID

Um cartão de ID (ou cartão , para encurtar) é uma coleção de arquivos que contêm todas as informações necessárias para permitir que um participante se conecte a uma rede de negócios. O cartão é referenciado como uma identidade. Antes que possa ser usado, ele precisa ser emitido para o usuário, permitindo que ele seja autenticado e autorizado a usar a rede. Os cartões são uma maneira muito prática de proteger o acesso a uma rede do Hyperledger Fabric. Em vez de manter uma senha (chamada de segredo na terminologia do Hyperledger Composer), você importa o cartão em uma coleção de cartões no Hyperledger Fabric chamada wallet. Deste ponto em diante, bastará referenciar o cartão para autenticar essa identidade.

O formulário geral para especificar um cartão é: userid@network, em que userid é o ID exclusivo do usuário e blockchain é a rede em que o usuário é autenticado.

Para manipular os dois níveis de segurança do Hyperledger Composer, no mínimo, serão necessários cartões para: (1) o PeerAdmin e (2) o administrador da rede de negócios.

PeerAdmin

O cartão PeerAdmin é um cartão de ID especial usado para administrar o Hyperledger Fabric local. Em uma instalação de desenvolvimento, como a do computador, o cartão de ID do PeerAdmin é criado quando o Hyperledger Fabric local é instalado.

O formato de um cartão do PeerAdmin para uma rede do Hyperledger Fabric v1.0 é PeerAdmin@hlfv1. Você já usou esse cartão anteriormente no tutorial quando implementou a iot-perishable-network no Hyperledger Fabric local.

Em geral, o PeerAdmin é uma função especial reservada para funções como:

  • Implementando redes de negócios
  • Criando, emitindo e revogando cartões de ID para administradores de rede de negócios

Como desenvolvedor, em uma instalação de produção do Hyperledger Fabric, você não teria acesso ao cartão PeerAdmin. Nesse caso, o administrador do Hyperledger Fabric implementaria sua rede de negócios, criaria os cartões de ID e assim por diante. Ao desenvolver e testar redes de blockchain usando o Hyperledger Fabric local, você usará o cartão de ID do PeerAdmin para executar essas funções.

Administrador da rede de negócios

Quando o PeerAdmin implementar a rede no Hyperledger Fabric, um cartão de ID será emitido para o administrador da rede de negócios e, em seguida, esse cartão será usado sempre que o administrador desta rede precisar fazer algo com a rede de negócios, como usar a interface da linha de comandos do Composer (que você usará em breve).

Lembra-se do cartão admin@iot-perishable-network que já foi mostrado? Esse era o cartão do administrador da rede de negócios emitido pelo PeerAdmin (ou seja, o administrador do Hyperledger Fabric local) quando a iot-perishable-network foi implementada.

Em geral, o administrador da rede de negócios é uma função especial reservada para funções como:

  • Atualizar a rede de negócios em execução
  • Consultar os vários registros (participantes, identidades e assim por diante)
  • Criar, emitir e revogar cartões de ID para participantes da rede de negócios

Isso mesmo, o cartão de ID do administrador também pode ser usado para emitir outros cartões de ID para participantes específicos (eu vou mostrar como fazer isso mais adiante no tutorial), para que todos os participantes tenham seus próprios cartões de ID. O acesso à rede pode ser controlado por esses cartões.

3b

Controle de acesso

Falando de controle de acesso, o Composer implementa o conceito de segurança baseada em função por meio de permissões inseridas diretamente em sua arquitetura para manipular a autenticação e a autorização. O acesso de um participante aos recursos é controlado com base na identidade que foi emitida para esse participante.

Nesta seção, você verá como configurar regras de controle de acesso para bloquear recursos na rede por participante, usando o arquivo de lista de controle de acesso (ACL) chamado permissions.acl (no momento da composição deste artigo, o arquivo deve ter este nome). Aqui estão os destaques:

  • O acesso é aplicado para conceder ou negar com base em recursos que correspondem à regra. Por padrão, se um recurso não corresponder a nenhuma regra, o acesso será negado.
  • O arquivo é processado de cima para baixo para que a primeira regra que concede ou nega o acesso a um recurso específico esteja em vigor e não possa ser substituída por uma próxima regra.
  • O formato de uma regra é bastante intuitivo. A palavra-chave rule indica o início de uma regra, seguida por um nome da regra, que precisa ser exclusivo.
  • A regra consiste em um conjunto de pares nome/valor que definem as propriedades da regra.

A Listagem 2 mostra o arquivo permissions.acl da rede de negócios iot-perishable-network.

Lista 2. permissions.acl da iot-perishable-network
                /**
                 * Sample access control list.
                 */
                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
                }

O formato geral para cada propriedade é property: "MATCH_EXPRESSION". As propriedades são:

description— um nome legível para a regra entre aspas duplas. Exemplo: description: "This is a description"

participante— Nome completo do participante ao qual o acesso é concedido ou negado, entre aspas duplas. Vários participantes podem ser especificados na MATCH_EXPRESSION por meio do uso do coringa de asterisco único (*) para indicar "todos", asterisco duplo (**) para indicar a recursão em um namespace ou ANY, que corresponde a todos os participantes em todos os namespaces.

Exemplos:

  • participant: "org.acme.shipping.perishable.Grower"— Aplique a regra somente a org.acme.shipping.perishable.Grower.
  • participant: "org.acme.shipping.perishable.*"— Aplique a regra a todos os participantes no namespace org.acme.shipping.perishable.
  • participant: "org.acme.shipping.**"— Aplique a regra a todos os participantes no namespace org.acme.shipping e recursivamente a qualquer namespace seguinte.
  • participant: "ANY"— Aplique a regra a todos os participantes em todos os namespaces.

operação— o MATCH_EXPRESSION pode ser um ou mais de CRIAR, READ, UPDATE, DELETEou ALL. Valores múltiplos podem ser separados por vírgulas (por exemplo, CREATE,READ concederá o acesso CREATE e READ ao recurso). Use ALL sozinho para indicar que a regra se aplica a todas as operações.

Exemplos:

  • operation: ALL— Aplique a regra a todas as operações CRUD.
  • operation: CREATE— Aplique a regra somente à operação CREATE
  • operation: READ,UPDATE— Aplique a regra às operações READ e UPDATE operações.

resource— define a "coisa" à qual a regra se aplica. Um recurso pode ser de qualquer classe (ou seja, um ativo, um participante ou uma transação) do modelo de negócio. Usando coringas, também é possível especificar várias classes, da mesma forma que para os participantes acima.

Exemplos:

  • resource: "org.acme.shipping.perishable.TemperatureReading"— A regra aplica-se somente à transação org.acme.shipping.perishable.TemperatureReading.
  • resource: "org.acme.shipping.perishable.*"— A regra aplica-se a todas as classes no namespace org.acme.shipping.perishable.
  • resource: "org.acme.shipping.**"— Aplique a regra a todos os recursos no namespace org.acme.shipping e recursivamente a qualquer namespace seguinte.

action— a ação que se aplica quando a regra é disparada. Um entre: ALLOW para conceder acesso ou DENY para negar o acesso dos participantes especificados aos recursos.

Exemplos:

  • action: ALLOW— Permita acesso aos recursos especificados.
  • action: DENY— Negue o acesso aos recursos especificados.

Observação: se a rede não tiver permissions.acl, o acesso será concedido a todos os participantes (ou seja, o acesso é amplamente aberto — não existe segurança no nível do recurso).

Você verá como usar regras mais adiante no tutorial. Além disso, a A documentação da ACL do Composer tem vários exemplos, portanto, não deixe de conferi-la.

Emita um novo ID - playground

Os cartões de ID podem ser um pouco abstratos e difíceis de visualizar. Portanto, vamos analisar um deles no playground (na verdade, você já analisou um, mas talvez não tenha percebido).

Inicie o playground do Hyperledger Composer e importe o iot-perishable-network (não se esqueça de criar o BNA executando npm install caso ainda não tenha feito isso). Você já importou uma rede na A Parte 2, portanto, consulte essa parte caso precise se lembrar.

Assegure-se de chamar a transação SetupDemo para criar os participantes e armazená-los no registro de participantes. Por quê? Porque não é possível emitir um cartão de ID para um participante que não está no registro. Para trabalhar com classes do modelo de negócio, deve-se instanciar o modelo, e é isso que o SetupDemo faz. Revise a seção "Testar a rede de negócios" da Parte 1 desta série de tutoriais se quiser saber mais sobre como trabalhar com modelos de negócios.

Quando o modelo for instanciado, no canto superior direito, onde aparece admin, clique para expandir a lista suspensa e selecione Registro de ID. Na próxima tela, escolha Emitir novo ID e, em seguida, insira grower1 como o Nome do ID e selecione farmer@email.com na lista suspensa. Veja a Figura 3.

Figura 3. Emita um novo cartão de ID no playground
Issue a new ID Card in Playground
Issue a new ID Card in Playground

Clique no botão Criar novo para emitir o ID. Quando o cartão de ID grower1 for emitido, ele será exibido em Registro de ID. Veja a Figura 4.

Figura 4. Novo cartão de ID grower1 no playground
New grower1 ID card in Playground
New grower1 ID card in Playground

Vou mostrar como emitir cartões de ID usando a CLI do Composer mais tarde no tutorial.

4

Interaja com a rede usando a CLI do Composer

Eu mostrei como instalar a interface da linha de comandos do Composer (CLI) na Parte 2 e ela já foi usada anteriormente neste tutorial para implementar iot-perishable-network e importar o cartão admin@iot-perishable-network. Agora, você usará a CLI para interagir com a rede de negócios iot-perishable-network. A CLI é muito prática porque ela pode ser usada em scripts e é muito fácil de usar, como você vai ver. Para usar a CLI, será necessário acessar a linha de comandos (Ubuntu) ou abrir uma janela do terminal (MacOS).

Efetue ping na rede

Depois de implementar a rede iot-perishable-network, será necessário enviar um ping a ela, o que não causa nenhum efeito colateral. Essa é uma maneira segura de verificar se a rede está funcionando. Insira este comando:

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

Se o botão ping o subcomando foi bem-sucedido, será exibida uma saída como esta:

                $ 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

Chame a transação SetupDemo

A transação SetupDemo é usada para instanciar o modelo. Insira este comando:

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

A saída será semelhante a esta:

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

Chame a transação TemperatureReading

A transação TemperatureReading é chamada pelo sensor de IoT no contêiner de envio sempre que a leitura da temperatura é realizada e precisa ser registrada no blockchain. Insira 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"}'

A saída será semelhante a esta:

                $ 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

Chame a transação ShipmentReceived

A transação ShipmentReceived é chamada pelo Importer quando o envio é recebido. Insira 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"}'

A saída será semelhante a esta:

                $ 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

Atualize a rede

Isso será prático sempre que você fizer uma mudança durante o desenvolvimento e precisar atualizar a rede implementada. Depois de executar npm install para recriar o BNA, insira este comando:

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

A saída será semelhante a esta:

                $ 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

Lembre-se de conferir a documentação da CLI para obter uma referência completa de comandos, exemplos e muito mais.

5

Junte tudo isso

Tudo o que você aprendeu até agora foi criar até este ponto. Agora você pode modificar a iot-perishable-network para transformá-la em um aplicativo de blockchain mais realístico. Aqui está o que você fará nesta seção:

  • Faça mudanças no modelo de rede:
    • Inclua novos participantes para representar os sensores de IoT no contêiner de envio
    • Inclua novas transações que modelam de forma mais realística um fluxo de trabalho de envio
    • Inclua novos eventos que são transmitidos quando certos pontos no fluxo de trabalho são atingidos
  • Inclua o chaincode para a nova transação
  • Modifique as permissões da rede
  • Inclua testes do recurso Cucumber para realizar testes de unidade das mudanças
  • Crie a rede e execute os testes de unidade
  • Implemente a rede no Hyperledger Fabric local
  • Emita os IDs para o grower, os sensores de temperatura e de GPS, o shipper e o importer
  • Execute transações usando a CLI

Ao concluir a modificação da iot-perishable-network, você terá aprendido as qualificações básicas necessárias para usar o Hyperledger Composer, você estará pronto para enfrentar um projeto de aplicativo de blockchain real, seu projeto causará inveja e as proezas do Hyperledger Composer e do Fabric serão amplamente divulgadas ganhando enorme proporção (ok, talvez eu esteja exagerando um pouco nesses dois últimos).

Se você ficar paralisado em algum ponto e precisar de alguma ajuda, confira o código da solução, que está em um projeto chamado iot-perishable-network-advanced que você já clonou em seu computador quando clonou o developerWorks projeto do GitHub.

Observação: é possível usar qualquer editor de sua preferência, mas eu vou usar o VSCode (que você instalou na Parte 2) para todas as mudanças nesta seção, e as instruções abaixo refletirão essas mudanças. Se você não estiver usando o VSCode, faça as adaptações necessárias.

5a

Faça as mudanças no modelo

Para fazer as mudanças listadas acima, você começará com o modelo. Abra o arquivo de modelo models/perishable.cto no VSCode e faça as mudanças abaixo. Observação: eu omiti os comentários dos fragmentos do modelo abaixo somente para poupar espaço neste tutorial. Se você verificar iot-perishable-network-advanced, verá que eu incluí comentários para cada elemento de modelo incluído e recomendo que você faça o mesmo.

Primeiro, modifique a transação ShipmentReceived para incluir uma propriedade opcional chamada receivedDateTime e, em seguida, inclua três novas transações que ampliam a ShipmentTransaction para atualizar o blockchain (livro-razão) quando um envio (1) é empacotado, (2) é selecionado para carregamento e (3) é carregado no envio do contêiner:

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

Em seguida, modifique o ativo Shipment para incluir quatro novas propriedades nas transações relacionadas ao envio para que, quando essas transações forem executadas, elas sejam armazenadas no blockchain com o Shipment. As linhas destacadas abaixo são aquelas que precisam ser incluídas (linhas 9 a 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
                }

Agora, inclua um participante abstrato para representar um dispositivo de IoT que é identificado por uma propriedade de sequência chamada deviceId e inclua duas subclasses dela: uma para representar um sensor de temperatura e outra para representar um sensor de GPS. Esses participantes atualizarão o blockchain com as respectivas leituras. Os dispositivos são participantes da rede? Isso mesmo! Um participante de uma rede de blockchain não precisa ser uma pessoa.

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

Finalmente, em um aplicativo de blockchain real, os eventos seriam emitidos em cada ponto importante no fluxo de trabalho: quando um envio fosse empacotado, quando ele fosse selecionado e assim por diante. Inclua quatro novos eventos para representar esses pontos no fluxo de trabalho de envio.

                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
                }

Isso é tudo para as mudanças no modelo. Em seguida, será necessário fazer algumas modificações nas transações JavaScript existentes e incluir o chaincode para as novas transações.

5b

Inclua o chaincode da transação

Até agora, todo o chaincode das redes com as quais você trabalhou estava contido no lib/logic.js. Obviamente, um aplicativo de blockchain real poderia ter muitas transações, fazendo com que o logic.js ficasse confuso e, ocasionalmente, difícil de ser mantido.

Desde que todo o código de transação esteja no diretório lib do modelo, o Hyperledger Composer não terá problema para resolver as chamadas de função. Para ilustrar isso, você fará algumas mudanças no logic.js e incluir mais alguns arquivos de origem Javascript.

No VSCode, clique com o botão direito no diretório lib , escolha Novo Arquivo e, em seguida, insira instantiateModelForTesting.js como o nome do arquivo. Agora, cole o cabeçalho a seguir, caso contrário, a "verificação de licença" da construção falhará, fornecendo uma mensagem de erro quando o código for construído mais tarde:

Lista 3. O cabeçalho de licença do Apache 2.0. Assegure-se de colar isso no novo arquivo JavaScript criado no projeto.
                /*
                 * Licensed under the Apache License, Version 2.0 (the "License");
                 * you may not use this file except in compliance with the License.
                 * You may obtain a copy of the License at
                 *
                 * http://www.apache.org/licenses/LICENSE-2.0
                 *
                 * Unless required by applicable law or agreed to in writing, software
                 * distributed under the License is distributed on an "AS IS" BASIS,
                 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                 * See the License for the specific language governing permissions and
                 * limitations under the License.
                 */

Agora, inclua as linhas de código a seguir para instanciar um TemperatureSensor e um GpsSensor como parte da configuração de teste. Inclua somente as linhas em destaque. Eu incluí linhas circundantes de código existente para que você possa localizá-las no código:

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

                // crie o sensor de temperatura
                var temperatureSensor = factory.newResource(NS, 'TemperatureSensor', 'SENSOR_TEMP001');
                // crie o sensor de GPS
                var gpsSensor = factory.newResource(NS, 'GpsSensor', 'SENSOR_GPS001');

                // crie o 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) {
                    // inclua os shippers
                    return shipperRegistry.addAll([shipper]);
                })
                .then(function() {
                    return getParticipantRegistry(NS + '.TemperatureSensor');
                })
                .then(function(temperatureSensorRegistry) {
                    // inclua os sensores de temperatura
                    return temperatureSensorRegistry.addAll([temperatureSensor]);
                })
                .then(function() {
                    return getParticipantRegistry(NS + '.GpsSensor');
                })
                .then(function(gpsSensorRegistry) {
                    // inclua os sensores de GPS
                    return gpsSensorRegistry.addAll([gpsSensor]);
                })
                .then(function() {
                    return getAssetRegistry(NS + '.Contract');
                })
                .then(function(contractRegistry) {
                    // inclua os contratos
                    return contractRegistry.addAll([contract]);
                })
                .
                .

Agora abra o logic.js, selecione o corpo da função inteiro de setupDemo e, em seguida, recorte-o de logic.js e cole-o no novo arquivo. Finalmente, renomeie a função como instantiateModelForTesting.

Em seguida, em logic.js, renomeie a função payOut como receiveShipment para que ela corresponda melhor à transação no modelo. Em seguida, inclua o código a seguir para emitir o ShipmentReceivedEvent logo após o bloco de código que calcula o dividendo. As linhas em destaque são aquelas que devem ser modificadas e/ou incluídas (modifique a linha 6, inclua as linhas 17 a 27). Eu incluí linhas circundantes de código existente para contextualizar:

                /**
                 * A shipment has been received by an importer
                 * @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';
                    // Store the ShipmentReceived transaction with the Shipment asset it belongs to
                    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) {
                            // atualize o saldo do grower
                            return growerRegistry.update(contract.grower);
                        })
                    .
                    .

Agora, será necessário incluir o chaincode JavaScript para as três novas transações incluídas no modelo, e você criará um novo arquivo de origem JavaScript para ele. No VSCode, clique com o botão direito do mouse no diretório lib e escolha Novo Arquivo, em seguida, insira shipment.js como o nome do arquivo. Inclua as funções abaixo para este arquivo. Assegure-se de colar o cabeçalho de comentários Apache 2.0 no topo do arquivo. Veja a Listagem 3.

Transação: ShipmentPacked código em cadeia

Primeiro, inclua uma função para manipular a transação ShipmentPacked. Quando o participante Grower (ou um de seus agentes autorizados) empacotar o envio para coleta e transporte, ele chamará a transação de blockchain ShipmentPacked para registrar este fato no livro-razão.

Quando essa transação é chamada, um ShipmentPackedEvent é emitido, notificando as partes interessadas e, em seguida, o chaincode atualiza o livro-razão.

Nomeie a nova função packShipment usando o idioma de nomenclatura de função "verb/object". No código JavaScript, os comentários mostrados abaixo são obrigatórios para o Hyperledger Composer reconhecer a função como uma transação.

Copiar todo o código abaixo e cole-o no shipment.js.

                /**
                 * ShipmentPacked transaction - invoked when the Shipment is packed and ready for pickup.
                 * 
                 * @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();
                
                    // Inclua a transação ShipmentPacked no
livro-razão (pelo ativo de Envio)
                    shipment.shipmentPacked = shipmentPacked;

                    // Crie a mensagem
                    var message = 'Shipment packed for shipment ' + shipment.$identifier;

                    // Efetue login no console do JavaScript //console.log(message);

                    // Emita uma notificação informando os
listeners inscritos que o envio foi empacotado
                    var shipmentPackedEvent = factory.newEvent(NS, 'ShipmentPackedEvent');
                    shipmentPackedEvent.shipment = shipment;
                    shipmentPackedEvent.message = message;
                    emit(shipmentPackedEvent);

                    // Atualize o registro do ativo
                    return getAssetRegistry(NS + '.Shipment')
                        .then(function (shipmentRegistry) {
                            // inclua a leitura de temperatura
no shipmentRegistry.update(shipment) do retorno do envio;
                        });
                }

Transação: ShipmentPickup código em cadeia

Em seguida, quando o participante Shipper (ou um de seus agentes autorizados) selecionar o envio empacotado para transporte, ele chamará a transação de blockchain ShipmentPickup para registrar este fato no livro-razão. Inclua uma função para manipular a transação ShipmentPickup chamada de pickupShipment.

Quando essa transação for chamada, um ShipmentPickupEvent é emitido, notificando as partes interessadas e, em seguida, o chaincode atualiza o livro-razão.

Copie todo o código abaixo e cole-o no shipment.js.

                /**
                 * ShipmentPickup - invoked when the Shipment has been picked up from the packer.
                 * 
                 * @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();
                
                    // Inclua a transação ShipmentPacked no
livro-razão (pelo ativo de envio)
                    shipment.shipmentPickup = shipmentPickup;

                    // Create the message
                    var message = 'Shipment picked up for shipment ' + shipment.$identifier;
                
                    // Efetue login no console JavaScript
                    //console.log(message);

                    // Emita uma notificação informando os
listeners inscritos de que o envio foi empacotado
                    var shipmentPickupEvent = factory.newEvent(NS, 'ShipmentPickupEvent');
                    shipmentPickupEvent.shipment = shipment;
                    shipmentPickupEvent.message = message;
                    emit(shipmentPickupEvent);

                    // Atualize o registro do ativo
                    return getAssetRegistry(NS + '.Shipment')
                        .then(function (shipmentRegistry) {
                            // inclua a leitura de temperatura
no shipmentRegistry.update(shipment) do retorno do envio;
                        });
                }

Transação: ShipmentLoaded código em cadeia

Finalmente, quando o participante Shipper (ou um de seus agentes autorizados) carregar o envio no contêiner, ele chamará a transação de blockchain ShipmentLoaded para registrar este fato no livro-razão. Inclua uma função para manipular o evento ShipmentLoaded chamada loadShipment.

Quando essa transação for chamada, um ShipmentLoadedEvent será emitido, notificando as partes interessadas e, em seguida, o chaincode atualizará o livro-razão.

Copie todo o código abaixo e cole-o no shipment.js.

                /**
                 * ShipmentLoaded - invoked when the Shipment has been loaded onto the container ship.
                 * 
                 * @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();
                
                    // Inclua a transação ShipmentPacked no
livro-razão (pelo ativo de Envio)
                    shipment.shipmentLoaded = shipmentLoaded;

                    // Crie a mensagem
                    var message = 'Shipment loaded for shipment ' + shipment.$identifier;

                    //  Efetue login no console do JavaScript
                    //console.log(message);

                    // Emita uma notificação informando os
listeners inscritos que o envio foi empacotado
                    var shipmentLoadedEvent = factory.newEvent(NS, 'ShipmentLoadedEvent');
                    shipmentLoadedEvent.shipment = shipment;
                    shipmentLoadedEvent.message = message;
                    emit(shipmentLoadedEvent);

                    // Atualize o registro do ativo
                    return getAssetRegistry(NS + '.Shipment')
                        .then(function (shipmentRegistry) {
                            // inclua a leitura de temperatura
no shipmentRegistry.update(shipment) do retorno do envio;
                        });
                }
5c

Mude as permissões de controle de acesso

Em um aplicativo de blockchain real, o ideal não é conceder permissão para nenhum participante fazer o que quer. Por exemplo, um Grower não deve ser capaz de chamar transações que apenas fazem sentido para um Shipper, e um sensor de GPS não deve ter permissão de registrar leituras de temperatura no blockchain.

Então, como evitar que isso aconteça? Em outras palavras, como criar sua rede de modo que você controle o acesso aos seus recursos? Por meio do arquivo da lista de controle de acesso (ACL).

Você já o viu nas Partes 1 e 2 desta série, mas eu apenas o mencionei e prometi abordá-lo com detalhes mais tarde. Bem, chegou a hora.

Chegou a hora de falar sobre controle de acesso, que começa com o permissions.acl. Você viu esse arquivo anteriormente, mas chegou a hora de colocar esse conhecimento em prática. Você modificará a iot-perishable-network para assegurar-se de que ele possa ser acessado somente por determinados participantes que realmente precisem acessar esse recurso. Como ilustração, eu resumi as configurações de controle de acesso que realmente são necessárias para iot-perishable-network na Tabela 1.

Talvez você ache que os nomes das regras são um pouco estranhos. Eu gosto de codificar o máximo possível de informações em um nome sem fazer com que ele fique extremamente longo ou completamente sem sentido. Eu penso o seguinte: a regra é nomeada como Participant_Operation_Resource. Portanto, Grower_R_Grower significa: "Conceder ao participante Grower o acesso READ para instâncias do Grower".

Tablela 1. Controle de acesso de participante— O tipo de acesso está entre parênteses: R = READ, U = UPDATE, C = CREATE
ParticipanteParticipantes acessados Ativos acessados Transações acessadas
GrowerGrower (R)Shipment (RU), Contract (RU)ShipmentPacked (C)
ShipperShipper (R)Shipment (RU), Contract (RU)ShipmentPickup, ShipmentLoaded (C)
ImporterImporter (R), Grower (RU)Shipment (RU), ContractShipmentReceived (C)
TemperatureSensorNONEShipment (RU), Contract (RU)TemperatureReading (C)
GpsSensorNONEShipment (RU), Contract (RU)GpsReading (C)

As regras são codificadas abaixo. Abra permissions.acl (na raiz do projeto de rede), exclua seu conteúdo atual e substitua-o pelas listagens seguintes. A listagem inteira é muito longa, portanto, vou abordar uma seção de cada vez.

As primeiras duas regras concedem permissão para os participantes do sistema Hyperledger Composer Participant e NetworkAdmin acessarem tudo no namespace org.hyperledger.composer.system e cada classe na rede, respectivamente.

Copie todo o código abaixo e cole-o no permissions.acl.

                /**
                 * System and Network Admin access rules
                 */
                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
                }

O próximo conjunto de regras concede permissões para os participantes da iot-perishable-network acessarem outros participantes (incluindo eles mesmos). Esse acesso varia de acordo com a relevância para as funções de negócios da rede. Por exemplo, o Grower apenas pode acessar a classe Grower, mas como o Importer executa a transação ShipmentReceived (que atualiza os saldos da conta do Importer e do Grower), o Importer precisa de acesso READ e UPDATE tanto para o Importer quanto para o Grower.

Copie todo o código abaixo e cole-o em permissions.acl.

                /**
                 * Rules for Participant registry access
                 */
                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
                }

A seguir estão as regras para o acesso aos ativos da rede. Nesse caso, eu desejo conceder acesso aos recursos Shipment e Contract para todos os participantes.

Copie todo o código abaixo e cole-o em permissions.acl.

                /**
                 * Rules for Asset registry access
                 */
                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
                }

A seguir estão as regras para chamar transações (que requerem o acesso CREATE). Como você pode ver, elas são analisadas caso a caso, conforme a necessidade. Por exemplo, o participante Grower não precisa acessar as transações do Shipper (e vice-versa).

Copie todo o código abaixo e cole-o em permissions.acl.

                /**
                 * Rules for Transaction invocations
                 */
                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
                }

A última regra diz (efetivamente): "Se houver algum recurso para o qual o acesso não tenha sido concedido acima, negar o acesso a ele". Esse é o comportamento padrão atual do Hyperledger Composer, mas se isso for alterado nos próximos releases, essas regras garantirá que a rede se comporte dessa forma, independentemente do comportamento padrão.

Copie todo o código abaixo e cole-o em permissions.acl.

                /**
                 * Make sure all resources are locked down by default.
                 * If permissions need to be granted to certain resources, that should happen
                 * above this rule. Anything not explicitly specified gets locked down.
                 */
                rule Default {
                    description: "Deny all participants access to all resources"
                    participant: "ANY"
                    operation: ALL
                    resource: "org.acme.shipping.perishable.*"
                    action: DENY
                }

Isso é tudo para permissions.acl. Salve o arquivo e prepare-se para realizar o teste de unidade dessas permissões.

5d

Inclua os testes de recursos

Espere, permissões de teste de unidade? Isso mesmo. Todas as permissões no permissions.acl podem ser submetidas a testes de unidade usando o Cucumber. Eu já apresentei o Cucumber na Parte 2 e agora você vai usá-lo para realizar o teste de unidade da nova lógica de transação e das regras de ACL incluídas em permissions.acl na seção anterior.

O interessante sobre o Cucumber é que sua sintaxe (Gherkin) é intuitiva, portanto, não é necessário explicar todos os testes que você verá.

Cenários básicos de teste

No VSCode, abra iot-perishable.feature, em seguida, exclua seu conteúdo e substitua-o pelas listagens abaixo ao trabalhar nesta seção.

Primeiro, você vai configurar o teste de recurso como é mostrado abaixo. Observe nas linhas 14 a 16 que o teste emite identidades (ou seja, cartões de ID) para os participantes especificados. Essas identidades serão usadas mais tarde no teste de unidade para testar as permissões de segurança que foram incluídas em permissions.acl na seção anterior.

Copie todo o código abaixo e cole-o em 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 a linha 28 na listagem acima. Eu recomendo que você sempre coloque uma "identidade padrão" nos testes do Cucumber para assegurar-se de que suas permissões de segurança estejam sempre sendo testadas (veja a barra lateral). Caso deseje, será possível substituir isso por um próximo "Quando eu uso a identidade xyz" em um teste de cenário específico, como será mostrado nesta seção.

O primeiro cenário executado será quando não houver leituras de temperatura fora do intervalo acordado. Observa na linha 2 que esse teste será executado como a identidade importer1, que é um Importer.

Copie todo o código abaixo e cole-o em 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}
                        ]
                        """

O próximo cenário testa o caso em que há uma leitura de temperatura abaixo do limite acordado. A quantia de dividendo deve ser menor (devido à penalidade de temperatura baixa no contrato).

Observe que o teste substitui a identidade padrão e é executado primeiramente como sensor_temp1, que é um participante TemperatureSensor (linha 2), em seguida, a identidade atual é alterada para importer1, que é um Importer (linha 6). Se o teste não for executado dessa forma, ele tentará executar a transação TemperatureReading como importer1 (que foi definido na seção de informações), que não tem permissão para chamar essa transação. Em seguida, a identidade deve ser alterada novamente ou a chamada de transação ShipmentReceived falhará por que um participante TemperatureSensor não tem permissão para chamar essa transação.

Copie todo o código abaixo e cole-o em 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}
                        ]
                        """

O próximo cenário testa o caso em que há uma leitura de temperatura acima do limite. Como o cenário anterior, esse também deve ser executado usando duas identidades separadas.

Copie todo o código abaixo e cole-o em 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}
                        ]
                        """

Por fim, a transação ShipmentReceived deve ser executada como um Importer (o único participante que tem permissão para chamá-la) e, quando ela for executada, um evento será emitido. A identidade é definida para este cenário na seção de informações.

Copie todo o código abaixo e cole-o em 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 |

Isso é tudo para o iot-perishable.feature. Salve o arquivo.

Testes para dispositivos de IoT

Os testes de recurso restantes funcionam basicamente da mesma forma que os já mostrados, portanto, não será necessário repetir a explicação. Para concluir essa seção, siga as orientações e cole as listagens como um todo nos novos arquivos .feature.

No VSCode, clique no diretório features e escolha Novo Arquivo, e chame o arquivo sensors.feature. Quando o arquivo for aberto na janela do editor, copie e cole a listagem a seguir no novo arquivo vazio:

Copie todo o código abaixo e cole-o em 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/

Agora salve este arquivo.

Testes relacionados ao Growers

No VSCode, clique no diretório features, escolha Novo Arquivo e chame o arquivo grower.feature. Quando o arquivo for aberto na janela do editor, copie e cole a listagem a seguir no novo arquivo vazio:

Copie todo o código abaixo e cole-o em 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/

Agora salve este arquivo. Observe a identidade padrão em uso (linha em destaque). Somente será necessário configurar uma identidade para um cenário específico se ele precisar de uma identidade diferente.

No VSCode, clique no diretório features, escolha Novo Arquivo e chame o arquivo shipper.feature. Quando o arquivo for aberto na janela do editor, copie e cole a listagem a seguir no novo arquivo vazio:

Copie todo o código abaixo e cole-o em 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/

Agora salve este arquivo.

Isso é tudo para os testes do recurso Cucumber.

5e

Crie e realize teste de unidade da rede

Agora chegou o momento de criar e realizar o teste de unidade da rede. Acesse uma linha de comandos (Ubuntu) ou abra uma janela do terminal (MacOS) e insira este comando:

npm install && npm test

Você já executou esses comandos na Parte 2 desta série de tutoriais. Uma saída grande será exibida, mas quando os testes de unidade forem executados, isto deverá ser exibido:

                  ✔ 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

As linhas 15 e 16 acima mostram que os 20 cenários (que consistem em 229 etapas) foram executados sem erros. Agora está tudo pronto para implementar a rede!

6

Implemente as mudanças na rede

Se você estiver acompanhando o tutorial, a iot-perishable-network já terá sido implementada no Hyperledger Fabric local e o modelo já terá sido instanciado. Infelizmente, será necessário incluir os participantes TemperatureSensor e GpsSensor no registro de participantes. Isso pode ser feito usando a CLI, mas eu quero mostrar como derrubar e limpar o Hyperledger Fabric local, que é algo que precisa ser feito periodicamente.

Quando o Hyperledger Fabric é derrubado usando o procedimento descrito abaixo, a rede e os materiais de criptografia gerados até agora são removidos. Isso não deve ser realizado em produção por motivos óbvios, mas como agora estamos no desenvolvimento, esse procedimento é algo que você precisa aprender.

Navegue para o diretório $COMPOSER_ROOT e insira o ./teardownFabric.sh . A saída será semelhante a esta:

                $ ./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

Agora limpe o diretório ~/.composer, que excluirá todas as placas e os materiais de criptografia gerados até agora. Em seguida, execute o script createPeerAdmin.sh novamente:

                $ 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

Em seguida, inicie o Hyperledger Fabric local executando o script startFabric.sh e, em seguida, navegue para o diretório iot-perishable-network, implemente a rede e importe o cartão admin@iot-perishable-network novamente:

                $ 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, será necessário executar a transação SetupDemo para instanciar o modelo, caso contrário, não haverá nenhum participante para emitir os cartões de ID!

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

De agora em diante, se você fizer mudanças na rede (desde que isso não envolva modificações em nenhuma das entradas no blockchain), bastará atualizá-la:

                $ 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

Emita IDs

Este é o momento de emitir os IDs. Você já fez isso no playground. Agora, você usará a CLI para emitir cartões de ID para os participantes mostrados na Tabela 2.

Tablela 2. Identidades de participantes a serem emitidas
ParticipanteIdentidadeNome do arquivo de cartão de ID
Growergrower1grower1.card
Shippershipper1shipper1.card
Importerimporter1importer1.card
TemperatureSensorsensor_temp1sensor_temp1.card
GpsSensorsensor_gps1sensor_gps1.card

Na seção da CLI, você chamou a transação SetupDemo, que instanciou a rede. Lembre-se de que não é possível emitir um ID para um participante que não está no registro de participantes.

Para emitir um cartão de ID para um participante, primeiro execute o comando composer identity issue, especificando o arquivo de cartão e, em seguida, importe o arquivo de cartão para a carteira eletrônica local. Use o cartão admin@iot-perishable-network para autenticação ao executar o composer identity issue .

O formato geral do comando para o iot-perishable-network é este:

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

Em que:

ID_CARD_FILE— é o nome do arquivo no qual o cartão de ID será armazenado (veja a Tabela 2).

IDENTITY— é a identidade que deve ser emitida (veja a Tabela 2).

PARTICIPANT_CLASS— é a classe do participante (por exemplo, Grower).

PARTICIPANT_ID— é o ID do participante quando ele foi instanciado no registro (por exemplo, farmer@email.com).

Cartão de ID: Grower

                $ 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

Cartão de ID: Shipper

                $ 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

Cartão de ID: Importer

                $ 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

Cartão 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

Cartão 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

Agora que você já emitiu cartões de ID para todos os participantes na rede e importou esses cartões na carteira eletrônica do Hyperledger Fabric local, será possível simular o fluxo de trabalho da rede de negócios Mercadorias Perecíveis de IoT usando a linha de comandos por meio da CLI.

Para ver os cartões emitidos, execute composer card list :

                $ 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

Envie transações

O formato geral para o comando composer transaction submit é:

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

Em que:

CARD_NAME— é o nome do cartão a ser usado (veja a Tabela 2).

DATA é o objeto JSON que contém os dados da transação. É possível copiar o formato de dados para qualquer transação do playground, remover novas linhas e substituir os valores de dados específicos (sim, foi assim que eu criei os dados das transações desta seção).

Envie transações

Nesta seção, você enviará as seguintes transações usando a CLI:

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

Cada uma dessas transações corresponde a uma etapa no fluxo de trabalho de movimentação de mercadorias perecíveis do Grower para o Importer.

Envie uma transação ShipmentPacked usando o cartão de ID grower1 para realizar a autenticação e a autorização na rede:

                $ 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 transação foi bem-sucedida porque o participante Grower tem permissão para executar a transação ShipmentPacked. Apenas por diversão, tente executar a transação usando o cartão de ID shipper1:

                $ 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

O participante Shipper não tem acesso à transação ShipmentPacked, portanto, a tentativa falhará, mostrando que as permissões codificadas anteriormente no tutorial estão funcionando conforme o esperado (é claro que você sabia disso quando executou o teste de unidade, mas é sempre bom ver em ação).

A segunda etapa no fluxo de trabalho de realizar um envio de mercadorias perecíveis do Grower para o Importer é selecionar o envio empacotado, que é manipulado pelo Shipper:

                $ 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

Após a seleção do envio, o Shipper o carregará no envio de contêiner e executará a transação ShipmentLoaded para registrar isso no livro-razão:

                $ 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

Enquanto isso, o participante TemperatureSensor está fazendo leituras dentro do contêiner de carga e registrando-as no livro-razão. Simule algumas delas desta forma:

                $ 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

Essas foram três transações TemperatureReading com a última em 11 C, que é 1 grau acima do limite do contrato, portanto, a penalidade de temperatura alta entrará em vigor quando o envio for recebido.

Agora, envie uma transação de GPS indicando que o envio do contêiner alcançou seu destino, que é o Porto de Nova York/Nova 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

A última etapa no fluxo de trabalho é para que o Importer receba o envio e chame a transação ShipmentReceived para registrar isso no 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

Isso não parece muito interessante, parece? Vá em frente e dispare o 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 ...

Agora aponte seu navegador para localhost:3000, localize o ativo de Envio e execute o /Get . Serão exibidas todas as transações enviadas, registradas no blockchain como parte do ativo de Envio. A Figura 5 mostra o que será exibido no corpo de resposta do /Get .

Figura 5. Ativo de envio mostrando as transações inseridas por meio da CLI
Shipment asset showing transactions entered                     through the CLI
Shipment asset showing transactions entered through the CLI

Há muitos dados, portanto, eu consegui mostrar apenas alguns na Figura 5, mas vá em frente, dispare o servidor REST você mesmo e role pelo ativo de Envio para ver todos os resultados.

Vídeo: demonstração de resultados:

Todo o conteúdo da seção anterior, mais o playground (quando aplicável), está agrupado neste vídeo:

Conclusão da Parte 3

Este tutorial teve uma grande abrangência. Primeiro, você instalou, iniciou e implementou o iot-perishable-network em uma instância local do Hyperledger Fabric.

Em seguida, você instalou e executou a interface REST, que pode ser usada para acessar a rede. Espero que você tenha conseguido assistir ao vídeo, no qual esse assunto é mais detalhado. Se você ainda não conseguiu, não deixe de ver.

Depois disso, você viu como o controle de acesso funciona no Hyperledger Composer, incluindo as regras de ACL e onde elas residem no código-fonte da rede.

Em seguida, você trabalhou com a interface da linha de comandos (CLI) para efetuar ping na rede em execução, executou o SetupDemo e outras transações e atualizou a rede à medida que fez mudanças em desenvolvimento.

Por fim, o mais importante: você modificou o iot-perishable-network para transformá-lo em um aplicativo de blockchain mais real, escreveu testes de recursos do Cucumber, emitiu IDs para todos os participantes e executou todas as transações usando a CLI.

Neste ponto, você deverá ter tudo o que precisa para começar a desenvolver seus próprios aplicativos de blockchain usando o Hyperledger Composer. Boa sorte!


Recursos para download


Temas relacionados


Comentários

Acesse ou registre-se para adicionar e acompanhar os comentários.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Internet of Things, Cloud computing
ArticleID=1057958
ArticleTitle=Fundamentos básicos do Hyperledger Composer, Parte 3: Implemente sua rede de blockchain localmente, interaja com ela e amplie-a
publish-date=02082018