Desenvolvimento

20 abr, 2017

Chaincode para desenvolvedores de Go – parte 01

Publicidade

Neste artigo, aprenda a desenvolver o chaincode usando o Golang para uma rede de blockchain baseada em Hyperledger Fabric v0.6. Vou falar dos fundamentos, como o papel do chaincode e das APIs para interagir com o Fabric subjacente, bem como tópicos avançados como modelagem de dados, controle de acesso e eventos.

Abundantes exemplos de código demonstram um pedido de empréstimo imobiliário e um processo de contrato de compra no blockchain. (Veja “Recursos para download” no final do texto para baixar a amostra completa do chaincode.)

Este tutorial é o primeiro de uma série; os tutoriais subsequentes irão abordar como testar unitariamente o seu chaincode e desenvolver aplicativos cliente que possam invocar o seu chaincode implementado.

O que é chaincode?

Chaincode, também chamado de contrato inteligente, é essencialmente a lógica de negócios que governa como as diferentes entidades ou partes em uma rede blockchain interagem ou transacionam umas com as outras. Simplificando, o chaincode é o encapsulamento de transações de rede de negócios em código. Invocações do chaincode resultam em sets e gets do ledger ou estado mundial.

No momento da publicação deste tutorial, o Hyperledger suporta escrever chaincode em Golang ou Java, que eventualmente roda dentro de um contêiner docker. Como o suporte ao chaincode para Java ainda está em versão beta, vou focar no Go.

Configurando seu ambiente de desenvolvimento

Siga as etapas na documentação do IBM Bluemix começando em “Configurando o ambiente de desenvolvimento“. Quando você chegar à seção intitulada “Configure seu pipeline de desenvolvimento”, pare lá; agora você está pronto para começar a desenvolver chaincode no Go.

Estrutura do Chaincode

Vamos dar uma olhada de perto na estrutura do chaincode. Conforme mencionado, o chaincode de exemplo na Listagem 1 e ao longo deste tutorial, bem como a arquitetura discutida, estão estritamente em conformidade com o preview v0.6 do Hyperledger Fabric.

A linha 4 da Listagem 1 importa o pacote shim em seu chaincode. O pacote shim fornece APIs que permitem que seu chaincode interaja com a rede de blockchain subjacente para acessar variáveis de estado, contexto de transação, certificados de chamados e atributos, e invocar outros chaincodes, entre outras operações.

Listagem 1. Chaincode de exemplo
package main
 
import "fmt"
import "github.com/hyperledger/fabric/core/chaincode/shim"
 
type SampleChaincode struct {
}
 
func (t *SampleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}
 
func (t *SampleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}
 
func (t *SampleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}
 
func main() {
    err := shim.Start(new(SampleChaincode))
    if err != nil {
        fmt.Println("Could not start SampleChaincode")
    } else {
        fmt.Println("SampleChaincode successfully started")
    }
 
}

Função Main

O ponto de partida para qualquer programa Go é a função Main e, portanto, é usado para fazer bootstrapp/iniciar o chaincode. Quando o par implementa sua instância do chaincode, a função Main é executada.

Conforme mostrado na linha 2 da Listagem 2, a linha shim.Start(new (SampleChaincode)) inicia o chaincode e o registra com o par. Você pode verificar isso localmente, executando o código em seu ambiente de desenvolvimento, que produzirá o seguinte erro: [shim] CRIT: peer.address não configurado, não pode se conectar ao par.

Listagem 2. Main ()
func main() {
    err := shim.Start(new(SampleChaincode))
    if err != nil {
        fmt.Println("Could not start SampleChaincode")
    } else {
        fmt.Println("SampleChaincode successfully started")
    }
 
}

O SampleChaincode é a estrutura que é necessária para implementar a interface shim.Chaincode, que possui três métodos – Init, Query e Invoke – para que ela possa ser considerada um tipo válido de Chaincode pelo pacote shim. Vejamos cada um dos três métodos.

Método Init/de Inicialização

O método Init é convocado quando o chaincode é implementado pela primeira vez na rede blockchain e será executado por cada par que implementa sua própria instância do chaincode. Esse método pode ser usado para qualquer tarefa relacionada à inicialização, bootstrapping ou configuração.

Lista 3. Init ()
func (t *SampleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}

Método Query

O método Query é invocado sempre que qualquer operação leitura/obtenção/consulta precisa ser executada no estado blockchain. Dependendo da complexidade do chaincode, esse método pode conter a lógica de leitura/obtenção/consulta ou pode ser terceirizado para separar métodos que podem ser invocados a partir do método Query.

O método Query não pretende alterar o estado do blockchain subjacente e, portanto, não é executado dentro de um contexto transacional. Se você tentar modificar o estado do blockchain dentro do método Query, um erro apontará a falta de um contexto transacional. Além disso, uma vez que este método apenas lê o estado do blockchain, as suas invocações não são registradas no blockchain.

Lista 4. Query ()

 

func (t *SampleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}

Método Invoke

O método Invoke é chamado sempre que o estado do blockchain está para ser modificado. Simplificando, todas as operações de criação, atualização e exclusão devem ser encapsuladas no método Invoke. Como ele modificará o estado do blockchain, o código Fabric do blockchain criará automaticamente um contexto de transação dentro do qual esse método será executado. Todas as invocações deste método são registradas no blockchain como transações, que finalmente são escritas em blocos.

Listagem 5. Invoke ()
func (t *SampleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}

Modelos de dados no chaincode

O ledger Hyperledger consiste em duas partes:

  1. Estado mundial, que é armazenado em um armazenamento de valor chave. Este armazenamento de valor chave é alimentado pelo RocksDB. Este armazenamento de valor de chave leva em uma array de bytes como o valor, que pode ser usado para armazenar uma estrutura serializada JSON. Essencialmente, este armazenamento de valor chave pode ser usado para armazenar qualquer modelo/esquema de dados personalizado exigido pelo seu contrato inteligente para funcionar.
  1. Blockchain, que consiste em uma série de blocos cada um contendo um número de transações. Cada bloco contém a hash do estado mundial e também está ligado ao bloco anterior. Blockchain é append-only.

A Listagem 6 mostra como criar modelos/schemas de dados personalizados. Ela define modelos de dados necessários para um pedido de empréstimo imobiliário. O modelo primário é chamado LoanApplication, que por sua vez tem tipos de dados primitivos e complexos, nomeadamente Informações Pessoais e Financeiras.

Uma vez que nosso armazenamento de key value armazena dados como JSON, esses modelos de dados acabariam precisando ser convertidos em uma string JSON. A anotação para cada campo, por exemplo, json:”id” age como metadados para a API marshal/unmarshal, que usará essas anotações para mapear cada campo com sua correspondente representação equivalente da sequência json.

Listagem 6. Código para criar modelos/esquemas de dados personalizados
//custom data models
type PersonalInfo struct {
    Firstname string `json:"firstname"`
    Lastname  string `json:"lastname"`
    DOB       string `json:"DOB"`
    Email     string `json:"email"`
    Mobile    string `json:"mobile"`
}
 
type FinancialInfo struct {
    MonthlySalary      int `json:"monthlySalary"`
    MonthlyRent        int `json:"monthlyRent"`
    OtherExpenditure   int `json:"otherExpenditure"`
    MonthlyLoanPayment int `json:"monthlyLoanPayment"`
}
 
type LoanApplication struct {
    ID                     string        `json:"id"`
    PropertyId             string        `json:"propertyId"`
    LandId                 string        `json:"landId"`
    PermitId               string        `json:"permitId"`
    BuyerId                string        `json:"buyerId"`
    SalesContractId        string        `json:"salesContractId"`
    PersonalInfo           PersonalInfo  `json:"personalInfo"`
    FinancialInfo          FinancialInfo `json:"financialInfo"`
    Status                 string        `json:"status"`
    RequestedAmount        int           `json:"requestedAmount"`
    FairMarketValue        int           `json:"fairMarketValue"`
    ApprovedAmount         int           `json:"approvedAmount"`
    ReviewerId             string        `json:"reviewerId"`
    LastModifiedDate       string        `json:"lastModifiedDate"`
}

Na segunda parte deste artigo daremos continuidade ao desenvolvimento do chaincode usando Golang para uma rede de blockchain baseada em Hyperledger Fabric v0.6. Até lá.

Recursos para download

 

***

Varun Ojha faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela Redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: https://www.ibm.com/developerworks/cloud/library/cl-ibm-blockchain-chaincode-development-using-golang/index.html?ca=drs-