DEV Community

Cover image for Getting started with Hyperledger Composer and private blockchains
Damien Cosset
Damien Cosset

Posted on • Edited on

Getting started with Hyperledger Composer and private blockchains

Introduction

In my last article, I started to explore what characteristics a blockchain would need to have to be helpful to a business, and a business network. Now it is time to dive in and use some solutions that are already existing.

Private blockchains or public blockchains?

There are several frameworks and tools that can be used to create a blockchain for a business network. We could go down the path of a public blockchain with Ethereum. It is a possible solution. However, in my learning, I chose to go down a different path and use a private blockchain framework. I believe private blockchains make more sense for businesses than public ones. The anonymity provided by the Ethereum blockchain ( for example ) isn't especially useful. The cryptocurrency also is not particularly useful. Some businesses will find a permissionless blockchain like Ethereum more attractive, but it makes more sense for networks to use a private blockchain.

In certain networks, you would need a permissioned blockchain that you can only join by invitation or permission. I chose to explore the Hyperledger framework, especially Fabric and Composer.

Hyperledger is an open source set of tools. It is hosted by the Linux Foundation and tries to explore how to use blockchains in a real-world business environment. There are a LOT of huge companies behind this thing ( IBM, Airbus, Intel, JPMorgan, Hitachi, Bosch ...).

Hyperledger Fabric is the first framework born out of this adventure. It is a blockchain implementation using container technology and smart contracts.

However, we will start by using a tool called Hyperledger Composer. Composer is built on top of Fabric and will allow us to build rapidly a business network using Javascript. There is also a very helpful playground in the browser that we will use.

What we will cover in this article

  • Create a business network using the differents Composer tools
  • Create some transactions with the Composer REST API
  • Create a simple application and use our business network with a REST API

Let's go!

Setting everything up

  • You will need Docker

  • You'll need the Composer CLI tools: npm install -g composer-cli

  • This will run a REST server to expose our business network:
    npm install -g composer-rest-server

  • Some things for generating code:

npm install -g generator-hyperledger-composer
npm install -g yo

  • Next, we'll need a local Hyperledger Fabric runtime, where our business network will be deployed.

Create a directory and let's name it fabric-tools

mkdir ~/fabric-tools && cd ~/fabric-tools

Inside this new directory, run the following command:

curl -O https://raw.githubusercontent.com/hyperledger/composer-tools/master/packages/fabric-dev-servers/fabric-dev-servers.zip
unzip fabric-dev-servers.zip

This gives you all you need to run a local Hyperledger Fabric runtime.

Run: ./downloadFabric.sh
Next, run: ./startFabric.sh, then ./createPeerAdminCard.sh

Great, we're all set up, we can start writing some code.

Creating a business network

You could choose to create your own business network from scratch. But, to speed things up, I made one for you. I chose a land registry network for my example.

In a network, there are three types of resources:

  • Participant: The types of entities that participate in the network. In my example, there are 5 differents types of participants:

    • PrivateIndividual: They buy and sell their properties
    • Bank: They afford loans ( or not )
    • Notary: They validate transactions
    • RealEstateAgent: They facilitate transactions between PrivateIndividuals
    • InsuranceCompany: You do need insurance on real estate right?
  • Assets: Assets are what transactions are made of. In this network, there are 3 different assets:

    • RealEstate: Because it's a land registry, remember?
    • Insurance: From the InsuranceCompany participants
    • Loan: From the Bank participant, you know, to finance the real estate thing
  • Transactions: Without transactions, nothing would move. Transactions make things happen. I have 3 differents transactions in my network:

    • BuyingRealEstate: One PrivateIndividual buys, another sells. What happens then?
    • ContractingLoan: Between a Bank and a PrivateIndividual
    • ContractingInsurance: Between a PrivateIndividual and an InsuranceCompany

Ok, this is the outline of our business network. Let's create it.

A business network archive is composed of 4 different types of files in Hyperledger:

  • Model file. Where you describe your participants, assets and transactions. We use a modeling language to describe this. Don't worry, it's not that complicated.

  • Access File. Hyperledger Fabric is a permissioned blockchain. We need to defined who gets to see what. For simplicity sake, we won't do anything crazy in this file. Everybody will see everything. In this, we use an Access Control Language (ACL) to express permissions.

  • Script File. Written in Javascript. This defines what happens when a transaction happens in the network.

  • Query File. Used to execute queries. Written in a native query language. We won't use this file for now. Another time.

Generating code

We'll use Yeoman to set up the code for us. Run:

yo hyperledger-composer:businessnetwork

Enter land-registry for the network name. Complete as you wish for the author fields.

Select Apache-2.0 as the license

Enter org.acme.landregistry as the namespace.

Great, now you have all the files you need.

The model file

Let's start by the model. This is where we define our participants, assets and transactions. Open up the org.acme.landregistry.cto file and put this in there:

**
 * Business network model
 */

namespace org.acme.landregistry

participant PrivateIndividual identified by id {
  o String id
  o String name
  o String address
  o Double balance default = 0.0
}

participant Bank identified by id {
  o String id
  o String name
  o Double balance default = 0.0
}

participant InsuranceCompany identified by id {
  o String id
  o String name
  o Double balance default = 0.0
}

participant Notary identified by id {
  o String id
  o String name
  o String address
  o Double balance default = 0.0
}

participant RealEstateAgent identified by id {
  o String id
  o String name
  o Double balance default = 0.0
  o Double feeRate
}

asset RealEstate identified by id {
  o String id
  o String address
  o Double squareMeters
  o Double price
  --> PrivateIndividual owner
}

asset Loan identified by id {
   o String id
   o Double amount
   o Double interestRate
   --> PrivateIndividual debtor
  --> Bank bank
  --> RealEstate realEstate
   o Integer durationInMonths
}

asset Insurance identified by id {
  o String id
  --> RealEstate realEstate
  --> PrivateIndividual insured
  --> InsuranceCompany insuranceCompany
  o Double monthlyCost
  o Integer durationInMonths
}

transaction BuyingRealEstate {
  --> PrivateIndividual buyer
  --> PrivateIndividual seller
  --> RealEstate realEstate
  --> Loan loan
  --> RealEstateAgent realEstateAgent
  --> Notary notary
  --> Insurance insurance
}

transaction ContractingInsurance {
  --> PrivateIndividual insured
  --> InsuranceCompany insuranceCompany
  --> RealEstate realEstate
  o Double monthlyCost
  o Integer durationInMonths
}

transaction ContractingLoan {
  --> PrivateIndividual debtor
  --> Bank bank
  --> RealEstate realEstate
  o Double interestRate
  o Integer durationInMonths
}

Enter fullscreen mode Exit fullscreen mode

Participants

Ok, there is quite a few things to uncover here. I think it reads relatively easily. First, I define our 5 participants types. They will all be uniquely identified by their id field. As you can see, this modeling language is strongly typed. You need to specify the type for each field. Nothing fancy for our participants, some String and Double types.

Assets

Our assets will also be identified by their id field. But, this time, you can see some arrows ( --> ) in the assets definitions. The arrows define relationships. A RealEstate asset has a relationship with a PrivateIndividual, its owner. A Loan has 3 differents relationships: the PrivateIndividual requesting a loan, the Bank that emits the loan and the RealEstate asset the loan finances.

Transactions

3 transactions in our model. They are not identified by any field like the other resources. The fields specified here will be passed to the functions declared in our script file. Let's see this now.

The Script File

Back to good old Javascript. This is where we define what happens during a transaction. Open the logic.js file in the lib folder:

'use strict';


/**
 * Contracting an insurance
 * @param {org.acme.landregistry.ContractingInsurance} insurance
 * @transaction
 */

 function contractingInsurance( insurance ){
    return getAssetRegistry('org.acme.landregistry.Insurance')
      .then(function(assetRegistry){
      var factory = getFactory()
      var insuranceId = insurance.insured.id + '' + insurance.insuranceCompany.id + '' + insurance.realEstate.id
      var insuranceAsset = factory.newResource('org.acme.landregistry', 'Insurance', insuranceId)
      insuranceAsset.insured = insurance.insured
      insuranceAsset.insuranceCompany = insurance.insuranceCompany
      insuranceAsset.realEstate = insurance.realEstate
      insuranceAsset.durationInMonths = insurance.durationInMonths
      insuranceAsset.monthlyCost = insurance.monthlyCost

      return assetRegistry.add(insuranceAsset)
    })
 }


/**
 * Contracting a loan
 * @param {org.acme.landregistry.ContractingLoan} loan
 * @transaction
 */

function contractingLoan( loan ){
  return getAssetRegistry('org.acme.landregistry.Loan')
    .then(function(assetRegistry){
    var factory = getFactory()
    var loanId = loan.debtor.id + '' + loan.realEstate.id + '' + loan.bank.id
    var loanAsset = factory.newResource('org.acme.landregistry', 'Loan', loanId) 
    loanAsset.debtor = loan.debtor
    loanAsset.bank = loan.bank
    loanAsset.interestRate = loan.interestRate
    loanAsset.durationInMonths = loan.durationInMonths
    loanAsset.realEstate = loan.realEstate
    loanAsset.amount = loan.realEstate.price

    return assetRegistry.add(loanAsset)
  })
}

/**
 * Buying Real Estate
 * @param {org.acme.landregistry.BuyingRealEstate} trade
 * @transaction
 */

function buyingRealEstate( trade ){
  var notaryFees = 0.1 * trade.realEstate.price
  var realEstateAgentFees = trade.realEstateAgent.feeRate * trade.realEstate.price
  var insuranceCostFirstMonth = trade.insurance.monthlyCost
  var totalCost = notaryFees + realEstateAgentFees + insuranceCostFirstMonth 
  // Updates the seller's balance
  trade.seller.balance += trade.realEstate.price

  // Check if the buyer has enough to pay the notary, real estate agent and insurance
  if( trade.buyer.balance < totalCost ){
    throw new Error('Not enough funds to buy this!')
  }
  trade.buyer.balance -= totalCost
  trade.realEstate.owner = trade.buyer
  trade.realEstateAgent.balance += realEstateAgentFees
  trade.notary.balance += notaryFees

  Promise.all([
    getAssetRegistry('org.acme.landregistry.RealEstate'),
    getParticipantRegistry('org.acme.landregistry.PrivateIndividual'),
    getParticipantRegistry('org.acme.landregistry.PrivateIndividual'),
    getParticipantRegistry('org.acme.landregistry.Notary'),
    getParticipantRegistry('org.acme.landregistry.RealEstateAgent')
  ]).then(function(registries){
    return (
      registries[0].update(trade.realEstate),
      registries[1].update(trade.seller),
      registries[2].update(trade.buyer),
      registries[3].update(trade.notary),
      registries[4].update(trade.realEstateAgent)
    )
  })
}
Enter fullscreen mode Exit fullscreen mode

Ok, we have three functions, one for each transaction. You use JSDoc description and tags to describe what transaction your function is supposed to work on. As you can see, you provide the namespace, { org.acme.landregistry.ContractingInsurance } for example, followed by @transaction.

contractingInsurance

In our model, to buy real estate, you first need to contract a loan and an insurance. This function creates an Insurance asset. We use the getAssetRegistry function to retrieve the insurance's asset registry. We then create a new Insurance asset and defines its properties. Finally, we add the new asset to the registry.

Note: All the registries that we use return promises.

contractingLoan

Exactly the same concept. Get the Loan registry, create a new Loan, add it. Note how I chose to dynamically create each asset ID. In a larger network, we would need to come up with something different. In our case, it'll be fine.

buyingRealEstate

Finally, we can buy some real estate. There are a few things going on here. First, I must explain a few rules about our network:

  • Everyone is paid during this transaction. Not before.

  • A notary must validate a transaction. She takes 10% of the real estate price as her fee. This is more or less what it costs in France in such transactions. So, if you buy a house for 100000€, you would need to pay an extra 10000€ to the notary.

  • Each transaction is conducted with a real estate agent. What the agent gets is specified in the model ( feeRate ).

  • The insurance has a montly cost. During the transaction, the buyer must pay the first month.

  • I assume that the buyer contracts a loan ONLY for the real estate price. Other costs must be paid by herself. As you can see in the function, if the buyer's balance can't cover the insurance's first month, the notary's fees and the real estate agent's fees, the transaction doesn't happen.

  • Transactions are atomic. Meaning, everything happens or nothing happens. If an error happens while we update one of our participants at the end of the transaction, we go back to the state we had before the transaction.

  • The rules I chose for the network are arbitrary. Some are based on how it works in France, others are chosen to make things a bit simpler.

In this function, we pay everyone. Then, we fetch every single registries we need and we update them all. Transaction done!

Permissions file

Finally, the permission file. We won't do anything crazy here. Just copy and paste this in the .acl file. We can define permissions depending on the participants, as you should in a private blockchain, but that would be outside the scope of this article. Put this in the permissions.acl file:

rule Default {
    description: "Allow all participants access to all resources"
    participant: "ANY"
    operation: ALL
    resource: "org.acme.landregistry.*"
    action: ALLOW
}

rule SystemACL {
  description:  "System ACL to permit all access"
  participant: "ANY"
  operation: ALL
  resource: "org.hyperledger.composer.system.**"
  action: ALLOW
}
Enter fullscreen mode Exit fullscreen mode

Basically, let everyone do whatever they want. Not what you would like in a production-ready blockchain, but good enough for now.

Deploying

Everything is ready! Now, we can deploy our business network in the Hyperledger Fabric. We'll need to run a few commands:

First, we need to create a Business Network Archive that the Fabric can use. To do this, in the land-registry folder, run this:

composer archive create -t dir -n .

This will create a .bna file.

Next, we'll install the composer runtime with:

composer network install --card PeerAdmin@hlfv1 --archiveFile land-registry@0.0.1.bna

The PeerAdmin card is the one you created by running /.createPeerAdminCard.sh earlier.

Next, we'll deploy the business network:

composer network start --networkName land-registry --networkVersion 0.0.1 --networkAdmin admin --networkAdminEnrollSecret adminpw --card PeerAdmin@hlfv1 --file networkadmin.card

Finally, we'll need to import the network administrator card into the network:

composer card import --file networkadmin.card

Playing around

Everything is ready. We can now create the REST API by running composer-rest-server.

  • Enter admin@land-registry as the card name.
  • Never use namespaces, then NO, then YES, then NO. ( Just YES to WebSockets )

You can navigate to localhost:3000/explorer now.

We have now access to a REST API to interact with our business network. As you can see, we have everything we defined earlier: paticipants, assets and transactions.

First things first, we need to create our participants and at least one RealEstate asset so we can make a transaction.

Let's go to the PrivateIndivual item, and select the /POST route. I'll create two participants here.

I'll name one PrivateIndividual Joe, with the id joe. I'll also give him 50000 in his balance. Give whatever address you want. Hit Try it out to create Joe.

The other individual will be Sophie, with the id sophie. She'll have 10000 in her balance. Give her an address and hit Try it out.

Make sure the response code is 200 every time. You can double check by going to the /GET route and fetch the data.

Let's move to the other participants now. The concept is the same, just jump between the different items.

The notary will be Ben, with the id ben. The real estate agent will be called Jenny, id jenny. Her feeRate will be set to 0.05 ( 5% ). The bank will be HSBC, id hsbc. Finally, the insurance company will be AXA, id axa.

Now, let's create a RealEstate asset. Same concept, got to the /POST route and give it the following data:

  {
    "$class": "org.acme.landregistry.RealEstate",
    "id": "buildingOne",
    "address": "France",
    "squareMeters": 100,
    "price": 100000,
    "owner": "resource:org.acme.landregistry.PrivateIndividual#sophie"
  }

Enter fullscreen mode Exit fullscreen mode

As you can see in the owner field, I decided to give this asset to Sophie, by specifying her id. This is the relationship between RealEstate and PrivateIndividual.

A transaction

Great, now, we can make a transaction! Sophie decides to sell her house, and Joe decides to buy it. First, Joe needs to go to his bank and contract a loan. In our network, we do not create a Loan asset directly. The transaction ContractingLoan is responsible for the creation of the asset. So choose the ContractingLoan transaction in the list, and the /POST route. To create our loan, give it the following data:

{
  "$class": "org.acme.landregistry.ContractingLoan",
  "debtor": "org.acme.landregistry.PrivateIndividual#joe",
  "bank": "org.acme.landregistry.Bank#hsbc",
  "realEstate": "org.acme.landregistry.RealEstate#buildingOne",
  "interestRate": 2.5,
  "durationInMonths": 300
}
Enter fullscreen mode Exit fullscreen mode

Again, you need to specify a few relationships for this transaction. The debtor is Joe, so I specify his id joe. The bank is hsbc and the real estate financed is buildingOne. I chose a 2.5% interest rate over 300 months ( 25 years ).

Note: Remember to give the participant or asset ID in the relationship. So => joe NOT Joe.!

Submit and you will see your new Loan if you go to the Loan items and use the /GET route.

Next, let's contract an insurance. The concept is the same, the transaction ContractingInsurance is responsible for the creation of the asset. Move to this item and choose the /POST route again:

{
  "$class": "org.acme.landregistry.ContractingInsurance",
  "insured": "org.acme.landregistry.PrivateIndividual#joe",
  "insuranceCompany": "org.acme.landregistry.InsuranceCompany#axa",
  "realEstate": "org.acme.landregistry.RealEstate#buildingOne",
  "monthlyCost": 150,
  "durationInMonths": 12
}
Enter fullscreen mode Exit fullscreen mode

Again, the insured is joe. The insuranceCompany is axa, and the realEstate is still buildingOne. I chose 150 for the monthly insurance's costs for a 12 months duration. Submit and check that the Insurance asset has been created by checking the /GET route under Insurance.

We finally have all the pre-requisites to execute our BuyingRealEstate transaction. Move to said transaction and give it the following data:

{
  "$class": "org.acme.landregistry.BuyingRealEstate",
  "buyer": "org.acme.landregistry.PrivateIndividual#joe",
  "seller": "org.acme.landregistry.PrivateIndividual#sophie",
  "realEstate": "org.acme.landregistry.RealEstate#buildingOne",
  "loan": "org.acme.landregistry.Loan#joebuildingOnehsbc",
  "realEstateAgent": "org.acme.landregistry.RealEstateAgent#jenny",
  "notary": "org.acme.landregistry.Notary#ben",
  "insurance": "org.acme.landregistry.Insurance#joeaxabuildingOne"
}
Enter fullscreen mode Exit fullscreen mode

Same concept, we specify the relationships in the transaction by adding the proper ids. joe is the buyer, sophie is the seller, jenny is the real estate agent, joebuildingOnehsbc is the loan's id, joeaxabuildingOne is the insurance's id, buildingOne is the real estate's id and ben is the notary's id.

When this transaction is submitted. You will see that the RealEstate asset has now a new owner Joe. You can also also see that the participant's balances have been updated:

  • Sophie's balance is now 110000 ( Her 10000 + the real estate's price of 100000 )

  • Joe's balance is now 34850. ( His 50000 - notary's fees of 10000 - real estate agent's fees of 5000 - the insurance's first month of 150 )

  • The notary's balance is now 10000

  • The real estate agent's balance is now 5000.

Great, we interacted with our business network and a blockchain. Now, let's create a simple User Interface that will interact with this REST API and retrieve some data.

Create a simple application with Composer and Fabric

We will not do anything too fancy, just something to show how straightforward it is to work with the business network in the Hyperledger Composer world.

This simple application will be built using the create-react-app tool. We will be able to do 4 things will it:

  • GET and display PrivateIndividual participants
  • GET and display RealEstate assets
  • POST and create new PrivateIndividual participants
  • POST and create new RealEstate assets

Of course, there are no limits to what you can do with the REST API, I chose arbitrarly which routes we will use here.

create-react-app

First, we need to install the create-react-app tool:

npm install -g create-react-app

Next, create your app:

create-react-app <APP_NAME>

This will create a React application in your app folder. You don't have to setup anything. Now, we need to change a couple of things to make sure we can talk to the REST API.

The proxy and port

We are going to retrieve data from our REST API running in localhost:3000. We will run our app in a different port. To make sure our React application goes to the right place to get the data and avoid cross-origin issues, go to your package.json file.

In the file, you will simply add the line:

"proxy": "http://localhost:3000/"

That's it for the proxy. Now, in that same file, look in the scripts object for the start command. The start command should look like this now:

"start": "PORT=3001 react-scripts start"

Because the REST API will run on port 3000, this will simply run the React app on the port 3001, and not ask us a different port everytime we start it.

The code!

I tried to make things very simple. Just raw code, no CSS, no fancy stuff. Two files: App.js and Client.js. You'll create the file Client.js and add this:

function search(query, cb) {
    return new Promise( (resolve,reject) => {
        return fetch(`api/${query}`, {
            accept: "application/json"
        })
            .then(parseJSON)
            .then(data => resolve(data));
    })

}

function create(type, data){
    return new Promise((resolve, reject) => {
        return fetch(`api/${type}`, {
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            method: 'post',
            body: JSON.stringify(data)
        })
        .then(parseJSON)
        .then(() => resolve())
    })

}

function parseJSON(response) {
  return response.json();
}

const Client = { search, create };
export default Client;
Enter fullscreen mode Exit fullscreen mode

What do we have here? Basically, we have two functions: search and create. The function search will handle our GET requests, the create one will handle our POST requests. If you didn't explore the REST API a bit, every route begins with the prefix /api. In our case, http://localhost:3000/api/, that's our prefix. The proxy key we added in our package.json will send every request to the right place. We just have to make sure the rest of our query is good.

So:

  • GET and POST PrivateIndividual => http://localhost:3000/api/PrivateIndividual
  • GET and POST RealEstate => http://localhost:3000/api/RealEstate

Now, let's move to the App.js file:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Client from './Client'

class App extends Component {

    state = {
        privates: [],
        realEstate: []
    }

    componentWillMount = () => {
        this.getPrivateIndividual()
        this.getRealEstate()
    }

    getPrivateIndividual = () => {
        Client.search('PrivateIndividual')
        .then(data => {
            this.setState({
                privates: data
            })
        })
    }

    getRealEstate = () => {
        Client.search('RealEstate')
        .then(data => {
            this.setState({
                realEstate: data
            })
            for( let i = 0; i < this.state.realEstate.length; i++ ){
                let privateIndividual = this.state.realEstate[i].owner.split('#')[1]
                Client.search(`PrivateIndividual/${privateIndividual}`)
                    .then(data => {
                        let realEstate = this.state.realEstate
                        realEstate[i].ownerName = data.name
                        this.setState({
                            realEstate
                        })
                    })
            }
        })
    }

    handlePrivateInputChange = e => {
        const {name, value} = e.target
        this.setState({
            [name]: value
        })
    }

    submitPrivate = () => {
        const data = {
            "$class": "org.acme.landregistry.PrivateIndividual",
            "id": this.state.name.toLowerCase(),
            "name": this.state.name,
            "address": this.state.address,
            "balance": this.state.balance
        }

        Client.create('PrivateIndividual', data)
        .then(() => {
            this.getPrivateIndividual()
        })
    }

    handleRealEstateInputChange = e => {
        const { value, name } = e.target
        this.setState({
            [name]: value
        })
    }

    submitRealEstate = () => {
        const data =  {
            "$class": "org.acme.landregistry.RealEstate",
            "id": this.state.id,
            "address": this.state.realEstateAddress,
            "squareMeters": this.state.squareMeters,
            "price": this.state.price,
            "owner": `org.acme.landregistry.PrivateIndividual#${this.state.owner}`
        }

        Client.create('RealEstate', data)
        .then(() => {
            this.getRealEstate()
        })
    }

  render() {

    return (
      <div className="App">
                <div>
                    <h2>Add PrivateIndividual</h2>
                    <label>Name:</label>
                    <input
                        onChange={this.handlePrivateInputChange}
                        type='text'
                        name='name' />
                    <label>Address:</label>
                    <input 
                        type='text' 
                        onChange={this.handlePrivateInputChange}
                        name='address' />
                    <label>Balance</label>
                    <input 
                        type='number' 
                        onChange={this.handlePrivateInputChange}
                        name='balance' />
                    <button onClick={this.submitPrivate}>Submit New PrivateIndividual</button>
                    <h2>Add RealEstate</h2>
                    <label>ID:</label>
                    <input
                        onChange={this.handleRealEstateInputChange}
                        type='text'
                        name='id' />
                    <label>Address:</label>
                    <input 
                        type='text' 
                        onChange={this.handleRealEstateInputChange}
                        name='realEstateAddress' />
                    <label>Square Meters</label>
                    <input 
                        type='number' 
                        onChange={this.handleRealEstateInputChange}
                        name='squareMeters' />
                    <label>Price:</label>
                    <input 
                        type='number'
                        onChange={this.handleRealEstateInputChange}
                        name='price' />
                    <label>Owner</label>
                    <input 
                        type='text'
                        onChange={this.handleRealEstateInputChange}
                        name='owner' />
                    <button onClick={this.submitRealEstate}>Submit New RealEstate </button>
                    <div style={{display: 'flex', justifyContent: 'space-around'}}>
                        <div>
                            <h3>Private Individual</h3> 
                            {this.state.privates.map((p, i) => (
                                <div
                                    style={{border: '1px solid black'}} 
                                    key={i}>
                                    <p>Name: {p.name}</p>
                                    <p>Balance: {p.balance}</p>
                                </div>
                            ))}
                        </div>
                        <div>
                            <h3>Real Estate Assets</h3>
                        {this.state.realEstate.map((r, i) => (
                                <div
                                    key={i} 
                                    style={{border: '1px solid red'}}>
                                    <p>ID: {r.id}</p>
                                    <p>Address: {r.address}</p>
                                    <p>Price: {r.price}</p>
                                    <p>Owner: {r.ownerName}</p>
                                </div>
                        ))}
                    </div>
                </div>
      </div>
            </div>
    );
  }
}

export default App;

Enter fullscreen mode Exit fullscreen mode

There is nothing crazy here either. The HTML is composed of two forms: one to create a new PrivateIndividual and another to create a new RealEstate asset. Below these, we loop through the data we have and create simple boxes to display our assets and participants.

In the componentWillMount function, we retrieve our data. submitPrivate and submitRealEstate do what they are told ;) .Notice the shape of the object we sent to the API.

Running the app

Make sure your REST API is running. If not, re-run composer-rest-server. Then, in the land-registry folder, run your React app with npm start.

Now, you can play along with your private blockchain!

Note: In the React application, I skipped things like error handling, CSS styling and other POST routes for transactions. The article seems long enough and, I hope, clear enough so that you could manage these things on your own. Have fun!

Top comments (25)

Collapse
 
prasadraavi profile image
prasadraavi

Hi Damien,

Firstly let me thank you for wonderful guidance on how to create blockchain business network.

I am halfway through but got stuck with deployment. I am getting the following error, could you please help me with the mistake I am doing and how can overcome that issue. Thanks in advance.


Issue :

I am getting the following error when I try to execute the commands for business network deployment. Can somebody help me with a solution for this. I am using ubuntu 16.04 ver.

Error :
dev1@localhost:~/land-registry$ composer archive create -t dir -n .
Creating Business Network Archive

Looking for package.json of Business Network Definition
Input directory: /home/dev1/land-registry
ParseException: Expected "namespace", comment, end of line or whitespace but "*" found. File /home/dev1/land-registry/models/org.acme.landregistry.cto line 19 column 1
Command failed

Collapse
 
eoghanr profile image
EoghanR • Edited

Hi prasadraavi,

I don't know if you still need this, but I'll put it here in case anyone else has this issue. In the .cto file, you need to have the line "namespace org.acme.landregistry". The same would need to be done with any other cto file by having namespace {your .cto file here} without the .cto attached.

I'm not sure if this is a universal solution, but it's the one that worked for me.

Best,
Eoghan

Collapse
 
ahmedmusallam profile image
Ahmed Musallam

Fantastic article! and your other articles on private blockchain are also very good!

I noticed that this article uses "composer runtime install" which is removed in the last release of the composer cli, it should now become "composer network install" with the appropriate options.

Collapse
 
damcosset profile image
Damien Cosset

You are right! Thank you for the catch! I will update the article accordingly.

Collapse
 
mohinimraut profile image
mohinimraut

When I restart my composer Rest Server I faced following error:
mohinir@rpqbsrv001:~/fabric-tools/landregistry$ composer-rest-server
? Enter the name of the business network card to use: admin@landregistry
? Specify if you want namespaces in the generated REST API: never use namespaces
? Specify if you want to use an API key to secure the REST API: No
? Specify if you want to enable authentication for the REST API using Passport: No
? Specify if you want to enable event publication over WebSockets: Yes
? 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@landregistry -n never -w true

Discovering types from business network definition ...
Connection fails: Error: Error trying to ping. Error: make sure the chaincode landregistry has been successfully instantiated and try again: getccdata composerchannel/landregistry responded with error: could not find chaincode with name 'landregistry'
It will be retried for the next request.
Exception: Error: Error trying to ping. Error: make sure the chaincode landregistry has been successfully instantiated and try again: getccdata composerchannel/landregistry responded with error: could not find chaincode with name 'landregistry'
Error: Error trying to ping. Error: make sure the chaincode landregistry has been successfully instantiated and try again: getccdata composerchannel/landregistry responded with error: could not find chaincode with name 'landregistry'
at _checkRuntimeVersions.then.catch (/home/mohinir/.nvm/versions/node/v8.11.3/lib/node_modules/composer-rest-server/node_modules/composer-connector-hlfv1/lib/hlfconnection.js:806:34)
at

Collapse
 
d8bhatta profile image
Deepak

Hello Damien,
This is really very helpful article to start hyperledger composer and understand rest api along with it. I have few questions:

  1. Participants and assets can be deleted as per composer (localhost:3000/explorer/#/) but not transactions. How participants and assets can be deleted if they are stored in blockchain?

  2. I have faced some validation issue like buyer doesn't have enough fund to buy the property, where have you written such validation in code? i dont see it in logic.js file.

  3. Do you have composer + fabric in nodejs rather than in react, It would be easier for me to create api using nodejs.

Thank you so much for such wonderful article.

Collapse
 
damcosset profile image
Damien Cosset

Thanks for the kind words.

1) I could be wrong on this, but I don't think it is possible to remove participants and assets from the blockchain. A distributed ledger is an append-only system. In a permissioned blockchain, you would simply remove the ability from a participant to make any sort of transactions.

2) This error should appear in the buyingRealEstate function. We check if the buyer has enough funds to pay the other participants.

3) I'll see what I can do :)

Collapse
 
gandharav profile image
gandharav

Great Article, While running the composer network start command I am getting some errors. We are running the network behind a corporate proxy and have already resolved DNS related issues , but not able to resolve this issue. Do you have any idea how this can be resolved basically the container creation is getting failed.

node-pre-gyp install --fallback-to-build --library=static_library

node-pre-gyp ERR! Completion callback never invoked!
node-pre-gyp ERR! System Linux 4.4.0-121-generic
node-pre-gyp ERR! command "/usr/local/bin/node" "/chaincode/output/node_modules/grpc/node_modules/.bin/node-pre-gyp" "install" "--fallback-to-build" "--library=static_library"
node-pre-gyp ERR! cwd /chaincode/output/node_modules/grpc
node-pre-gyp ERR! node -v v8.9.4
node-pre-gyp ERR! node-pre-gyp -v v0.10.0
node-pre-gyp ERR! This is a bug in node-pre-gyp.
node-pre-gyp ERR! Try to update node-pre-gyp and file an issue if it does not help:
node-pre-gyp ERR! github.com/mapbox/node-pre-gyp/issues
npm WARN hscdb2@0.0.1 No repository field.
npm WARN The package composer-common is included as both a dev and production dependency.

npm ERR! code ELIFECYCLE
npm ERR! errno 6
npm ERR! grpc@1.11.3 install: node-pre-gyp install --fallback-to-build --library=static_library
npm ERR! Exit status 6
npm ERR!
npm ERR! Failed at the grpc@1.11.3 install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /root/.npm/_logs/2018-05-17T05_58_38_825Z-debug.log
"
error starting container

Collapse
 
zainabas profile image
zainabAS

Really helpful article!!
Thank You
I have a question regarding the logic.js file, I couldn't find it in the lib folder. Should I create it?

Collapse
 
damcosset profile image
Damien Cosset

Yes, you should :)

Collapse
 
zainabas profile image
Comment marked as low quality/non-constructive by the community. View Code of Conduct
zainabAS

Another question.
When I reached to "Deploying" step and open "localhost:3000/explorer" I didn't get the same result. I didn't find the assets, participants and transactions.It is just the "System: General business network methods"that is there.
Any idea what would be my mistake?

Many thanks.

Thread Thread
 
damcosset profile image
Damien Cosset

I'm not sure. Could you check that you are using the same composer version everywhere? When I wrote this article, the composer version was 0.16 I believe. It is now 0.19 or so. If the code is the same as mine, that might be an issue.

Thread Thread
 
zainabas profile image
zainabAS

I was using compser 0.19 with the old fabric version "hlfv1".
I changed it to the new one "hlfv11" and the problem is solved.

Thanks.

Collapse
 
sbtmentor profile image
SBTMentor • Edited

Hello Damien!

Excellent article! However,after buying real estate,sophie's balance is not being updated. Notary balance too is being shown as 0! Can you please tell where the problem is?

Thanks in advance!

Collapse
 
haso71 profile image
haso71

Hallo Damien, hopefully you could help me! As a beginner I have some installation problems: I set up a brand new Ubuntu 16.04 and got far (with so deprecation warnings) throw your guide until:
1.) Starting ./createPeerAdminCard.sh: brings some kind of warning: "Need to have composer-cli installed at v0.19 or greater"

2.) "composer archive create -t dir -n ." brings an error:
module.js:549 throw err; Error: Cannot find module './api' ...

I didn´t find any api-files, so whats wrong?

Collapse
 
damcosset profile image
Damien Cosset • Edited

Hey there.

For the first problem, you most likely need to update your composer-cli package version to 0.19. Or install it if you haven't: npm install -g composer-cli or npm update -g composer-cli should get the job done.

I'm not sure about the second one though. Try again with the updated version of composer. If you still have an error, could you show the entire error message?

Note: I updated the article for the new composer network install command.

Good luck!

Collapse
 
haso71 profile image
haso71

thank you for the fast answer, so I tried to check all versions on my system, for my opinion, i already had composer-cli installed at v0.19.0 before.

// -- version check ----
1.)
haso71@ubuntu:~/hyperledger/fabric-tools$ docker-compose --version
docker-compose version 1.20.1, build 5d8c71b
haso71@ubuntu:~/hyperledger/fabric-tools$ yo --version
2.0.1
haso71@ubuntu:~/hyperledger/fabric-tools$ nodejs -v
v8.11.1
haso71@ubuntu:~/hyperledger/fabric-tools$ node -v
v8.11.1
haso71@ubuntu:~/hyperledger/fabric-tools$ npm -v
5.6.0
haso71@ubuntu:~/hyperledger/fabric-tools$ go version
go version go1.6.2 linux/amd64

haso71@ubuntu:~/hyperledger/fabric-tools$ sudo npm install -g composer-cli
npm WARN deprecated fs-promise@1.0.0: Use mz or fs-extra3.0 with Promise Support
/usr/bin/composer -> /usr/lib/node_modules/composer-cli/cli.js
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.1.3 (node_modules/composer-cli/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

haso71@ubuntu:~/hyperledger/fabric-tools$ sudo ./createPeerAdminCard.sh
Development only script for Hyperledger Fabric control
Running 'createPeerAdminCard.sh'
FABRIC_VERSION is unset, assuming hlfv11
FABRIC_START_TIMEOUT is unset, assuming 15 (seconds)

Need to have composer-cli installed at v0.19 or greater

2.)
haso71@ubuntu:~/hyperledger/fabric-tools/land-registry$ composer archive create -t dir -n .
module.js:549
throw err;
^
Error: Cannot find module './api'
at Function.Module._resolveFilename (module.js:547:15)
at Function.Module._load (module.js:474:25)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
at Object. (/usr/lib/node_modules/composer-cli/node_modules/node-report/index.js:3:13)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
haso71@ubuntu:~/hyperledger/fabric-tools/land-registry$ ls -l
total 32
drwxrwxr-x 3 admin admin 4096 Apr 2 01:34 features
drwxrwxr-x 2 admin admin 4096 Apr 2 01:46 lib
drwxrwxr-x 2 admin admin 4096 Apr 2 01:43 models
-rw-r--r-- 1 admin admin 878 Apr 2 01:34 package.json
-rw-r--r-- 1 admin admin 356 Apr 2 01:49 permissions.acl
-rw-rw-r-- 1 admin admin 1588 Apr 2 01:49 permissions(original).acl
-rw-r--r-- 1 admin admin 57 Apr 2 01:34 README.md
drwxrwxr-x 2 admin admin 4096 Apr 2 01:34 test
haso71@ubuntu:~/hyperledger/fabric-tools/land-registry$

Collapse
 
pallavrj profile image
pallavrj

Hi Damien,

Great Article :)

Question: Whenever I do Network Install, It gives me below error. Any help will be highly appreciated.

$ composer network install --card PeerAdmin@hlfv1 --archiveFile land-registry@0.0.1.bna
x Installing business network. This may take a minute...
Error: Error trying install business network. Error: No valid responses from any peers.
Response from attempted peer comms was an error: Error: 14 UNAVAILABLE: Connect Failed
Command failed

I have Below settings:

Docker Toolbox Version: 18.03.0-ce
Hyperledger fabric Version: 1.1.0
Fabric_Version: hlfv11
Peeradmin card: PeerAdmin@hlfv1

Please let me know in case you need further details to guide me.

Regards,
Pallav Raj

Collapse
 
lokeshgene profile image
lokeshgene

Hi Damien - very good article!, thanks for putting this together!. I have few questions, please do assist.

1) When we create react app, can this be in separate folder?

2) I followed the exact way you did, but after I triggered the react-app. It shows the following error.

Unhandled Rejection (SyntaxError): Unexpected token < in JSON at position 0

it is occured at Client.js while parsing JSON. Do we need to make any other special adjustment

Some comments may only be visible to logged-in visitors. Sign in to view all comments.