Develop a DApp on Hyperledger

Develop a DApp on Hyperledger

In the first tutorial in our Hyperledger Fabric series, we set up our very own network, created two organizations each with two peers, created a simple token smart contract, and sent some tokens around. We recommend going through it first before proceeding with this one. Don’t worry, it doesn’t take long.

In this tutorial we’re going to take it a step further. We’re going to simulate building a full blockchain-based application complete with a decentralized network, a smart contract recording application state to the ledger, and client-side application code that interacts with it programatically. Let’s dig in.

What is a DApp?

A “DApp” is short for a “decentralized application.” It is simply any app that runs on the blockchain. Most people know about the blockchain as just a ledger that records information. For example, in Bitcoin, the blockchain records transactions between two parties, i.e. how many Bitcoins one party sent another.

However, the blockchain is much more powerful than that. “Smart contracts” are computer programs that sit on the blockchain, waiting for a sender to send in an input. The conditional logic contained in the smart control then prints an output based on the state of variables contained in the smart contract.

For example, a simple smart contract could add 1 to an integer sent to it, if the current day is Monday. It can add 2 to an integer if the current day is Tuesday. If I sent the integer 100 to the smart contract on a Tuesday, 102 would get printed on the blockchain and the data would be secure and immutable.

Now let’s say I create an iPhone app where I can easily send in the integer I want, and then view the results of the blockchain. This app would not rely on any central server but rather the entire blockchain network. No one would “own” the reads and writes that happen in this app. This app would be a decentralized application, or a DApp.

Easy, right? Let’s try playing with a DApp on the Hyperledger blockchain!

This Dapp will allow us to keep a list of cool cars. We’ll then modify who owns these cars, and everyone in our network will know exactly who owns which cars.

Create a new network

Before we go any further, it’s best to clean up any remnants from our previous experiments. If you followed along the previous tutorial, you’ll need to ensure that network is completely shut down and removed before proceeding.

~$ cd fabric-samples/first-network
first-network$ ./byfn.sh -m down
first-network$ docker rm -f $(docker ps -aq)
first-network$ docker network prune

What did we do?

The byfn.sh script is a helper script for the first-network tutorial that we haven’t previously used (preferring to do it manually for a more intense learning experience!). Here we are using it to quickly and completely shut down the network. We are then removing any other running docker containers, networks, and associated images. If some of these return errors, that’s ok — it just means there was nothing to tidy up.

first-network$ cd ../fabcar
fabcar$ ./startFabric.sh

What did we do?

We’ve switched to the fabcar tutorial directory and run a script that sets up the network. This script goes through similar steps to what we manually slogged through in the first tutorial. Isn’t it nice to have a script do it for us? Of course you can open the script and give it a read if you want to know exactly what it’s doing.

Install NodeJS

The client application we’re using is written in Javascript and we’ll be executing it in NodeJS. If you don’t have it, you’ll need to install it.

Install the SDK

The client application is going to leverage some Javascript libraries to facilitate interacting with the Hyperledger Fabric CA (certificate authority) and network peers. Let’s install these libraries now.

fabcar$ npm install

What did we do?

The node package manager (npm) has installed javascript libraries called fabric-client and fabric-ca-client into the node_modules directory, so our scripts can find them.

Configure the Hyperledger Fabric Network

Let’s create an admin user identity:

fabcar$ node enrollAdmin.js
Store path:/root/fabric-samples/fabcar/hfc-key-store
Successfully enrolled admin user “admin”
Assigned the admin user to the fabric client
… [verbose json formatted output] …

What did we do?

The enrollAdmin.js script communicates with Hyperledger Fabric’s CA (certificate authority) to generate certificate material for an admin user, and then uses the certificates to enroll the admin user in the network. The certificates are stored locally so that they can be used when our application code subsequently interacts with the network.

Great! Now let’s create a regular user identity:

fabcar$ node registerUser.js
Store path:/root/fabric-samples/fabcar/hfc-key-store
Successfully loaded admin from persistence
Successfully registered user1 — secret:YZkGydDhQMOh
Successfully enrolled member user “user1”
User1 was successfully registered and enrolled and is ready to interact with the fabric network

What did we do?

The registerUser.js script uses the admin credentials we created in the previous step to request a new user identity. Once again, it saves certificate material locally so our application can use it for subsequent interactions.

Reading from the ledger

Since our application is storing data in a ledger on the blockchain, one of the most basic operations it will need to do is query data from the ledger.

fabcar$ node query.js
Store path:/root/fabric-samples/fabcar/hfc-key-store
Successfully loaded user1 from persistence
Query has completed, checking results
Response is [
{
"Key": "CAR0",
"Record": {
"colour": "blue",
"make": "Toyota",
"model": "Prius",
"owner": "Tomoko"
}
},
{
"Key": "CAR1",
"Record": {
"colour": "red",
"make": "Ford",
"model": "Mustang",
"owner": "Brad"
}
},
...
]

What did we do?

The query.js script uses the user credentials we created in the previous step to perform a query on the ledger. It sends this request to a network endpoint acting as a peer on the network, which has full access to the ledger state. It printed all the cars that we have sitting in our ledger. Every node in the network that runs query.js will see the exact same results, even though none of these nodes “own” the data. Pretty cool huh?

If you open up query.js, you’ll see that the particular query we performed was this:

const request = {
chaincodeId: 'fabcar',
fcn: 'queryAllCars',
args: ['']
};

The query runs the queryAllCars function on the smart contract called fabcar, with no arguments. Let’s see if we can modify it to call a different function.

Go ahead and modify a copy of query.js around line 54 to look like this, and save it as a separate file, called query2.js:

const request = {
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR7']
};

And run it:

fabcar$ node query2.js
Store path:/root/fabric-samples/fabcar/hfc-key-store
Successfully loaded user1 from persistence
Query has completed, checking results
Response is {"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}

It works! Through this interface, a client application can make RPC (remote procedure calls) to a smart contract’s chaincode running within a blockchain network.

So how do we inspect the smart contracts that are letting us print out this car collection? How would we modify the smart contracts themselves if we wanted to?

By reviewing the smart contract’s chaincode located at ../chaincode/fabcar/go/fabcar.go we can see the smart contract functions and the parameters they take. Note, Hyperledger’s chaincode is written in Go, where the calls to the contracts we’ve been making are in Javascript. Hyperledger does have Go support for contract calls but the documentation and support are not very good yet.

Let’s get familiar with chaincode a little bit. Let’s inspect the changeCarOwner function in ../chaincode/fabcar/go/fabcar.go

func (s *SmartContract) changeCarOwner(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

        if len(args) != 2 {
                return shim.Error("Incorrect number of arguments. Expecting 2")
        }

        carAsBytes, _ := APIstub.GetState(args[0])
        car := Car{}

        json.Unmarshal(carAsBytes, &car)
        car.Owner = args[1]

        carAsBytes, _ = json.Marshal(car)
        APIstub.PutState(args[0], carAsBytes)

        return shim.Success(nil)
}

This function allows us to change the owner of a car. It takes 2 string arguments, the index of the car (e.g. CAR0) and the new owner. The function then updates the ledger with the new owner and returns a success code.

This smart contract is an example provided by the good folks at the Linux Foundation, the creators of Hyperledger. Do you see any ways to improve this code? You’ll notice that one of the arguments takes in an uncapped slice of strings args []string but the function errors if the slice has a length of anything other than 2. If you only want to allow 2 string variables, you’re better off making that explicit in the function signature. See? Even the experts can use a good code review once in a while 🙂

Let’s next call this smart contract and change an owner.

Writing to the ledger

Unless our application is a read-only application, we’re probably going to want to write some application state back to the ledger, so that it can persist with the durability and auditability guarantees for which we’ve chosen blockchain technology in the first place.

There is a script called invoke.js that lets us call a function that mutates the state on the blockchain. Let’s run it:

fabcar$ node invoke.js
Store path:/root/fabric-samples/fabcar/hfc-key-store
Successfully loaded user1 from persistence
Assigning transaction_id: c26de2800d3fcd8261df6bcc1ab9e8a0170ff577b2f677fe0c40b3de7c1bda56
Transaction proposal was bad
Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...
Failed to invoke successfully :: Error: Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...

It produces an error because we first need to edit the script and formulate a command. Open it in your text editor and have a look near line 62. Edit the command to look like the following (but put your own name instead of ‘Cory Heath’).

var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: 'changeCarOwner',
args: ['CAR0', 'Cory Heath'],
chainId: 'mychannel',
txId: tx_id
};

Run the script again and watch it succeed!

fabcar$ node invoke.js
Store path:/root/fabric-samples/fabcar/hfc-key-store
Successfully loaded user1 from persistence
Assigning transaction_id: 2f6d75fab2b4d18b89c8f541dcbe88b9f3d029336203d5a00abac100cb1af80f
Transaction proposal was good
Successfully sent Proposal and received ProposalResponse: Status - 200, message - ""
The transaction has been committed on peer localhost:7053
Send transaction promise and event listener promise have completed
Successfully sent transaction to the orderer.
Successfully committed the change to the ledger by the peer

What did we do?

Just like query.jsinvoke.js uses the user credentials we created at the beginning of our tutorial, and communicates with a network endpoint which is a peer on the network. It formulates a command to call a function belonging to the API exposed by the fabcar smart contract. This function takes two arguments: a car, and an owner.

A peer has a full copy of the blockchain state and can respond to query commands, but a command which mutates the state needs to also talk to an orderer node. That’s why we couldn’t just edit query.js, and that’s why invoke.js is a bit longer.

Just to be sure our change took effect, let’s run the query command again:

fabcar$ node query.js
Store path:/root/fabric-samples/fabcar/hfc-key-store
Successfully loaded user1 from persistence
Query has completed, checking results
Response is [
{
"Key": "CAR0",
"Record": {
"colour": "blue",
"make": "Toyota",
"model": "Prius",
"owner": "Cory Heath"
}
},
...
]

Note the owner of the first car — it worked!

Great job!

You’ve just talked to a smart contract programatically from Javascript! Our mission in this tutorial was to take a tour of the basic architecture of a distributed app backed by a Hyperledger Fabric blockchain.

In this tutorial we started a Hyperledger Fabric network that had the fabcar smart contract. This is an example smart contract which implements a simple database of cars. It exposes a simple API that lets us query the cars, query a specific car, change a car’s owner, among other functions. We used Javascript programs to interact with the fabcar smart contract running on the network, by making requests to network endpoints running on peers.

Source link