Blockchain: what should we build a PoC?

Eyes are afraid, and hands itch!

In previous articles, we dealt with the technologies on which blockchains are built (What should we build a blockchain?) and cases that can be implemented with their help (What does it cost us to build a case?). It's time to work with your hands! For the implementation of pilots and PoC (Proof of Concept), I prefer to use clouds, because. they have access from anywhere in the world and, often, there is no need to spend time on the tedious installation of the environment, because. There are preset configurations. So, let's make something simple, like a network for transferring coins between participants and call it modestly Citcoin. To do this, we will use the IBM cloud and the Hyperledger Fabric universal blockchain. First, let's figure out why Hyperledger Fabric is called a universal blockchain?

Blockchain: what should we build a PoC?

Hyperledger Fabric - Universal Blockchain

Generally speaking, a universal information system is:

  • A set of servers and a software core that performs business logic;
  • Interfaces for interaction with the system;
  • Means for registration, authentication and authorization of devices / people;
  • Database storing operational and archived data:

Blockchain: what should we build a PoC?

The official version of what Hyperledger Fabric is can be read at Online, and in short, Hyperledger Fabric is an open source platform that allows you to build private blockchains and execute arbitrary smart contracts written in JS and Go programming languages. Let's take a closer look at the Hyperledger Fabric architecture and make sure that this is a universal system that only has specifics for storing and recording data. The specificity lies in the fact that data, as in all blockchains, is stored in blocks that are placed in the blockchain only if the participants have reached a consensus and after recording, the data cannot be quietly corrected or deleted.

Hyperledger Fabric Architecture

The diagram shows the Hyperledger Fabric architecture:

Blockchain: what should we build a PoC?

Organizations - organizations contain peers, so blockchain exists due to the support of organizations. Different organizations can be part of the same channel.

Channel - a logical structure that unites peers into groups, thus. blockchain is set. Hyperledger Fabric can simultaneously process multiple blockchains with different business logic.

Membership Services Provider (MSP) is a CA (Certificate Authority) for issuing identity and assigning roles. To create a node, you need to interact with the MSP.

Peer nodes — verify transactions, store the blockchain, execute smart contracts and interact with applications. Peers have an identity (digital certificate) issued by the MSP. Unlike the Bitcoin or Etherium network, where all nodes are equal, nodes play different roles in Hyperledger Fabric:

  • Peer can be endorsing peer (EP) and execute smart contracts.
  • Committing peer (CP) - only save data in the blockchain and update the "World state".
  • Anchor Peer (AP) - if several organizations participate in the blockchain, then anchor peers are used to communicate between them. Each organization must have one or more anchor peers. With the help of the AP, any peer in an organization can obtain information about all peers in other organizations. Used to synchronize information between APs. gossip protocol.
  • Leader Peer - if the organization has several peers, then only the leader of the peer will receive blocks from the Ordering service and give them to the rest of the peers. The leader can be set statically or dynamically selected by peers in the organization. The gossip protocol is also used to synchronize leader information.

Assets — entities of value that are stored in the blockchain. More specifically, this is key-value data in JSON format. It is this data that is recorded in the Blockchain blockchain. They have a history that is stored on the blockchain and a current state that is stored in the "World state" database. Data structures are filled arbitrarily depending on business tasks. There are no required fields, the only recommendation is that assets must have an owner and be of value.

Ledger - consists of the Blockchain blockchain and the Word state database, which stores the current state of the assets. World state uses LevelDB or CouchDB.

Smart contract — with the help of smart contracts, the business logic of the system is implemented. In Hyperledger Fabric, smart contracts are called chaincode. With the help of chaincode, assets and transactions over them are specified. In technical language, smart contracts are software modules implemented in JS or Go programming languages.

Endorsement policy - for each chaincode, you can set policies for how many and from whom you need to expect confirmations for the transaction. If the policy is not set, then the default is: “the transaction must be confirmed by any member of any organization in the channel”. Policy examples:

  • The transaction must be confirmed by any administrator of the organization;
  • Must be confirmed by any member or client of the organization;
  • Must confirm any peer of the organization.

Ordering service - packs transactions into blocks and sends them to peers in channel. Ensures message delivery to all peers on the network. Used for industrial systems Kafka message broker, for development and testing Solo.

call flow

Blockchain: what should we build a PoC?

  • The application interacts with Hyperledger Fabric using Go, Node.js or Java SDK;
  • The client creates a transaction tx and sends it to endorsing peers;
  • The peer verifies the client's signature, completes the transaction, and sends the endorsement signature back to the client. Chaincode is executed only on the endorsing peer, and the result of its execution is sent to all peers. Such an algorithm of work is called - PBFT (Practical Byzantine Fault Tolerant) consensus. Differs from classic BFT the fact that the message is sent and confirmation is expected not from all participants, but only from a certain set;
  • After the client has received the number of responses corresponding to the endorsement policy, it sends the transaction to the Ordering service;
  • Ordering service forms a block and sends it to all committing peers. The Ordering service ensures that blocks are written sequentially, which eliminates the so-called ledger fork (see section "Forks");
  • Peers receive the block, check the endorsement policy again, write the block to the blockchain, and change the state in the “World state” DB.

Those. it turns out the division of roles between nodes. This ensures the scalability and security of the blockchain:

  • Smart contracts (chaincode) perform endorsing peers. This ensures the confidentiality of smart contracts, as it is not stored by all participants, but only by endorsing peers.
  • Ordering should work quickly. This is ensured by the fact that Ordering only forms a block and sends it to a fixed set of leader peers.
  • Committing peers only store the blockchain - there can be many of them and they do not require much power and instant work.

More architectural solutions of Hyperledger Fabric and why it works this way and not otherwise can be found here: Architecture Origins or here: Hyperledger Fabric: A Distributed Operating System for Permissioned Blockchains.

So, Hyperledger Fabric is a truly universal system with which you can:

  • Implement arbitrary business logic using the smart contract mechanism;
  • Write and receive data from the blockchain database in JSON format;
  • Grant and validate API access using a Certificate Authority.

Now that we've got a bit of the Hyperledger Fabric specifics out of the way, let's finally do something useful!

Deploying the blockchain

Formulation of the problem

The task is to implement the Citcoin network with the following functions: create an account, get a balance, replenish an account, transfer coins from one account to another. Let's draw an object model, which we will further implement in a smart contract. So, we will have accounts that are identified by names (name) and contain a balance (balance), and a list of accounts. Accounts and a list of accounts are in terms of Hyperledger Fabric assets. Accordingly, they have a history and a current state. I'll try to draw it visually:

Blockchain: what should we build a PoC?

The top figures are the current state, which is stored in the "World state" database. Below them are figures showing the history that is stored in the blockchain. The current state of assets is changed by transactions. The asset only changes in its entirety, so as a result of the transaction, a new object is created, and the current value of the asset goes into history.

IBM Cloud

We create an account in IBM cloud. To use the blockchain platform, it must be upgraded to Pay-As-You-Go. This process may not be fast, because IBM requests additional information and verifies it manually. On the positive side, I can say that IBM has good training materials that allow you to deploy Hyperledger Fabric in their cloud. I liked the following series of articles and examples:

The following are screenshots of the IBM Blockchain platform. This is not an instruction for creating a blockchain, but simply a demonstration of the scope of the task. So, for our purposes, we make one Organization:

Blockchain: what should we build a PoC?

We create nodes in it: Orderer CA, Org1 CA, Orderer Peer:

Blockchain: what should we build a PoC?

We start users:

Blockchain: what should we build a PoC?

Create a Channel and call it citcoin:

Blockchain: what should we build a PoC?

In essence, Channel is a blockchain, so it starts from the zero block (Genesis block):

Blockchain: what should we build a PoC?

Writing a Smart Contract

/*
 * Citcoin smart-contract v1.5 for Hyperledger Fabric
 * (c) Alexey Sushkov, 2019
 */
 
'use strict';
 
const { Contract } = require('fabric-contract-api');
const maxAccounts = 5;
 
class CitcoinEvents extends Contract {
 
    async instantiate(ctx) {
        console.info('instantiate');
        let emptyList = [];
        await ctx.stub.putState('accounts', Buffer.from(JSON.stringify(emptyList)));
    }
    // Get all accounts
    async GetAccounts(ctx) {
        // Get account list:
        let accounts = '{}'
        let accountsData = await ctx.stub.getState('accounts');
        if (accountsData) {
            accounts = JSON.parse(accountsData.toString());
        } else {
            throw new Error('accounts not found');
        }
        return accountsData.toString()
    }
     // add a account object to the blockchain state identifited by their name
    async AddAccount(ctx, name, balance) {
        // this is account data:
        let account = {
            name: name,
            balance: Number(balance),       
            type: 'account',
        };
        // create account:
        await ctx.stub.putState(name, Buffer.from(JSON.stringify(account)));
 
        // Add account to list:
        let accountsData = await ctx.stub.getState('accounts');
        if (accountsData) {
            let accounts = JSON.parse(accountsData.toString());
            if (accounts.length < maxAccounts)
            {
                accounts.push(name);
                await ctx.stub.putState('accounts', Buffer.from(JSON.stringify(accounts)));
            } else {
                throw new Error('Max accounts number reached');
            }
        } else {
            throw new Error('accounts not found');
        }
        // return  object
        return JSON.stringify(account);
    }
    // Sends money from Account to Account
    async SendFrom(ctx, fromAccount, toAccount, value) {
        // get Account from
        let fromData = await ctx.stub.getState(fromAccount);
        let from;
        if (fromData) {
            from = JSON.parse(fromData.toString());
            if (from.type !== 'account') {
                throw new Error('wrong from type');
            }   
        } else {
            throw new Error('Accout from not found');
        }
        // get Account to
        let toData = await ctx.stub.getState(toAccount);
        let to;
        if (toData) {
            to = JSON.parse(toData.toString());
            if (to.type !== 'account') {
                throw new Error('wrong to type');
            }  
        } else {
            throw new Error('Accout to not found');
        }
 
        // update the balances
        if ((from.balance - Number(value)) >= 0 ) {
            from.balance -= Number(value);
            to.balance += Number(value);
        } else {
            throw new Error('From Account: not enought balance');          
        }
 
        await ctx.stub.putState(from.name, Buffer.from(JSON.stringify(from)));
        await ctx.stub.putState(to.name, Buffer.from(JSON.stringify(to)));
                 
        // define and set Event
        let Event = {
            type: "SendFrom",
            from: from.name,
            to: to.name,
            balanceFrom: from.balance,
            balanceTo: to.balance,
            value: value
        };
        await ctx.stub.setEvent('SendFrom', Buffer.from(JSON.stringify(Event)));
 
        // return to object
        return JSON.stringify(from);
    }
 
    // get the state from key
    async GetState(ctx, key) {
        let data = await ctx.stub.getState(key);
        let jsonData = JSON.parse(data.toString());
        return JSON.stringify(jsonData);
    }
    // GetBalance   
    async GetBalance(ctx, accountName) {
        let data = await ctx.stub.getState(accountName);
        let jsonData = JSON.parse(data.toString());
        return JSON.stringify(jsonData);
    }
     
    // Refill own balance
    async RefillBalance(ctx, toAccount, value) {
        // get Account to
        let toData = await ctx.stub.getState(toAccount);
        let to;
        if (toData) {
            to = JSON.parse(toData.toString());
            if (to.type !== 'account') {
                throw new Error('wrong to type');
            }  
        } else {
            throw new Error('Accout to not found');
        }
 
        // update the balance
        to.balance += Number(value);
        await ctx.stub.putState(to.name, Buffer.from(JSON.stringify(to)));
                 
        // define and set Event
        let Event = {
            type: "RefillBalance",
            to: to.name,
            balanceTo: to.balance,
            value: value
        };
        await ctx.stub.setEvent('RefillBalance', Buffer.from(JSON.stringify(Event)));
 
        // return to object
        return JSON.stringify(from);
    }
}
module.exports = CitcoinEvents;

Intuitively, everything should be clear here:

  • There are several functions (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance) that the demo program will call using the Hyperledger Fabric API.
  • The SendFrom and RefillBalance functions generate events (Event) that the demo program will receive.
  • The instantiate function is called once when the smart contract is instantiated. In fact, it is called not once, but every time the version of the smart contract changes. Therefore, initializing a list with an empty array is a bad idea, because now when changing the version of the smart contract, we will lose the current list. But nothing, I'm just learning).
  • Accounts and the list of accounts (accounts) are JSON data structures. JS is used for data manipulation.
  • You can get the current value of an asset by calling the getState function, and update it with putState.
  • When creating an Account, the AddAccount function is called, in which a comparison is made for the maximum number of accounts in the blockchain (maxAccounts = 5). And there is a jamb (noticed?), Which leads to an infinite increase in the number of accounts. Mistakes like this should be avoided

Next, we load the smart contract into the Channel and instantiate it:

Blockchain: what should we build a PoC?

We look at the transaction for installing the Smart Contract:

Blockchain: what should we build a PoC?

See details about our Channel:

Blockchain: what should we build a PoC?

As a result, we obtain the following scheme of the blockchain network in the IBM cloud. Also on the diagram there is a demo program running in the Amazon cloud on a virtual server (details about it will be in the next section):

Blockchain: what should we build a PoC?

Creating a GUI for Hyperledger Fabric API Calls

Hyperledger Fabric has an API that can be used to:

  • Create a channel;
  • Peer connections to channel;
  • Installation and instantiation of smart contracts in the channel;
  • Calling transactions;
  • Request information on the blockchain.

Application development

In our demo program, we will use the API only to call transactions and request information, because. we have already done the rest of the steps using the IBM blockchain platform. We write a GUI using the standard technology stack: Express.js + Vue.js + Node.js. You can write a separate article about how to start creating modern web applications. Here I will leave a link to a series of lectures that I liked the most: Full Stack Web App using Vue.js & Express.js. The result is a client-server application with a familiar graphical interface in the style of Google's Material Design. The REST API between client and server consists of several calls:

  • HyperledgerDemo/v1/init - initialize the blockchain;
  • HyperledgerDemo/v1/accounts/list - get a list of all accounts;
  • HyperledgerDemo/v1/account?name=Bob&balance=100 - create Bob account;
  • HyperledgerDemo/v1/info?account=Bob - get information about Bob account;
  • HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 - transfer two coins from Bob to Alice;
  • HyperledgerDemo/v1/disconnect - close the connection to the blockchain.

API description with examples put on Postman website is a well-known program for testing HTTP API.

Demo application in the Amazon cloud

The application was uploaded to Amazon, because IBM has still not been able to upgrade my account and allow me to create virtual servers. How the domain was attached to the cherry: www.citcoin.info. I'll keep the server turned on for a bit, then turn it off, because cents for rent are dripping, and citcoin coins are not yet listed on the exchange) I put demo screenshots in the article so that the logic of work is clear. The demo application can:

  • Initialize the blockchain;
  • Create an Account (but now a new Account cannot be created, because the maximum number of accounts specified in the smart contract has been reached in the blockchain);
  • Get a list of Accounts;
  • Transfer citcoin coins between Alice, Bob and Alex;
  • Receive events (but now there is no way to show events, so for simplicity it is written in the interface that events are not supported);
  • Log actions.

First, we initialize the blockchain:

Blockchain: what should we build a PoC?

Next, we start our account, do not trifle with the balance:

Blockchain: what should we build a PoC?

We get a list of all available accounts:

Blockchain: what should we build a PoC?

We select the sender and recipient, we get their balances. If the sender and recipient are the same, then his account will be replenished:

Blockchain: what should we build a PoC?

In the log, we monitor the execution of transactions:

Blockchain: what should we build a PoC?

Actually, with a demo program, that's all. Next, you can see our transaction in the blockchain:

Blockchain: what should we build a PoC?

And the general list of transactions:

Blockchain: what should we build a PoC?

With this, we have successfully completed the implementation of the PoC to create the Citcoin network. What else needs to be done to make Citcoin a full-fledged coin transfer network? Very little:

  • At the stage of creating an account, implement the generation of a private / public key. The private key must be stored by the user account, which is public on the blockchain.
  • Make a transfer of coins, in which not a name, but a public key is used to identify the user.
  • Encrypt transactions going from the user to the server with his private key.

Conclusion

We have implemented the Citcoin network with the following functions: add an account, get a balance, replenish your account, transfer coins from one account to another. So what did it cost us to build a PoC?

  • It is necessary to study the blockchain in general and Hyperledger Fabric in particular;
  • Learn how to use IBM or Amazon clouds;
  • Learn the JS programming language and some web framework;
  • If some data needs to be stored not in the blockchain, but in a separate database, then learn how to integrate, for example, with PostgreSQL;
  • And last but not least - without knowledge of Linux in the modern world, nowhere!)

Of course, not rocket science, but you have to sweat!

Sources on GitHub

Sources put on GitHub. Brief description of the repository:
Catalog «server» - Node.js server
Catalog «client» - Node.js client
Catalog «blockchain» (values ​​of parameters and keys, of course, are non-working and are given only for example):

  • contract — smart contract source
  • wallet - user keys for using the Hyperledger Fabric API.
  • *.cds - compiled versions of smart contracts
  • *.json files - examples of configuration files for using the Hyperledger Fabric API

It's only the beginning!

Source: habr.com

Add a comment