Andrei Anisimov

@andrei.anisimov

Choosing private blockchain tech: Hyperledger Composer

Hyperledger Composer architecture

Welcome, to part two of the “Choosing private blockchain tech” series (part1 is here). In each article we implement a simple use case to give an overview of what an existing technology does and how it can be useful in businesses.

Originally, this article was meant to be about Hyperledger Fabric, but after learning about Hyperledger Composer, it became clear that it would be beneficial to write about it first. This will allow us to get a higher-level view of what type of business applications can be built on top of Hyperledger Fabric blockchain and make it easier to understand the purpose of Fabric itself.

The business use case

Let’s start with the artificially simple use case from the finance industry defined in part1. Here are the parties in this scenario:

  • Non-custodial asset manager (hedge fund): makes investment decisions (trades). Non-custodial means they don’t actually hold a client’s money. Instead, after a trade is executed they send directions to a custodian that moves the money appropriately.
  • Custodian: Usually a large and trusted finance institution that holds a client’s money and executes transfers on behalf of the hedge fund. A custodian needs to know about each trade to distribute payments to counterparties.
  • Client(s): Invests into the hedge fund. Each client needs to view trades in their own account, but not other clients’ accounts.

Let’s see how we can use Hyperledger Composer to define all participants in this scenario and enable them to share appropriate information.

What is Hyperledger Composer?

Hyperledger Composer is a higher-level toolset and framework made to quickly build and run applications on top of Hyperledger Fabric blockchain. If Fabric is network-level, Composer is application-level. It allows us to define the data model, business logic and access control lists for an application that can be deployed and executed inside of a Fabric channel. Users of such applications don’t have to run a local node and can interact with a remote node through an RPC or HTTP REST if needed.

Composer comes with a nice web playground which allows users to prototype applications in browser without setting up a local network. We’ll use the playground to implement and test our use case. In a future article, we will export the resulting ‘business network definition’ and deploy to a real Hyperledger Fabric blockchain.

Solution

The full code of the solution can be found here. Bellow we’ll see how to configure each piece step-by-step using the Composer Playground.

1. Create network

Select ‘Deploy a new business network’ and fill out the required info. In my case, the name of the network is ‘hedge-fund-network’ and admin card name is admin@hedge-fund-network. Select the ‘empty-business-network’ template and hit Deploy. This will create an empty business network and an admin identity that has full control over it. After the business network is created we can connect to it by pressing ‘Connect Now’.

2. Configure object model

Composer has its own object modeling language that is very straightforward and easy to use. There are 4 types of resources that can be defined:

  • Assets. Things that are being tracked in the application. In our case we track Lots — traded blocks of securities.
  • Participants. Entities that interact with the network. Each has their own permissions.
  • Transactions are sent to update an asset or a participant as well as to execute a custom-defined logic.
  • Events can be emitted from the transaction logic and subscribed to by participants.

Now, let’s create a model file named ‘org.acme.mynetwork.cto’ and add the following code. The file defines 3 participant types (Trader, Client, Custodian), Lot asset, Trade transaction and NewTrade event.

/**
* My commodity trading network
*/
namespace org.acme.mynetwork
asset Lot identified by lotId {
o String lotId
o String securityName
o Double quantity
o Double price
--> Client owner
}
participant Client identified by clientId {
o String clientId
o String description
}
participant Custodian identified by custodianId {
o String custodianId
o String description
}
participant Trader identified by traderId {
o String traderId
o String name
}
transaction Trade {
--> Trader trader
--> Client client
--> Lot lot
}
event NewTradeEvent {
--> Lot lot
}

3. Configure transaction logic

Next, we’ll implement a custom logic that gets executed every time a Trade transaction is sent. Create a new script file called script.js with the code below. Composer knows to execute this code for each Trade transaction based on the @param annotation in the comment. This code does 2 things: changes the owner of a Lot and emits a NewTrade event on success.

/**
* Track the trade
* @param {org.acme.mynetwork.Trade} trade — the trade to be processed
* @transaction
*/
function tradeCommodity(trade) {
var factory = getFactory();
trade.lot.owner = trade.client;
var result = getAssetRegistry(‘org.acme.mynetwork.Lot’)
.then(function (assetRegistry) {
return assetRegistry.update(trade.lot);
});
if (result) {
var newTradeEvent = factory.newEvent(‘org.acme.mynetwork’, ‘NewTradeEvent’);
newTradeEvent.lot = trade.lot;
emit(newTradeEvent);
}
return result;
}

4. Access control list (ACL)

Lastly, we need to define an access control list governing what each participant type can do and see. Here we specify that traders can execute trades, clients can view their own trades, and custodians can view all trades.

/* Admin */
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
}
/* Common */
rule CommonReadTransactionRegistry {
description: "Allow all participants to read transaction registry"
participant: "org.hyperledger.composer.system.Participant"
operation: READ
resource: "org.hyperledger.composer.system.TransactionRegistry"
action: ALLOW
}
rule CommonReadParticipantRegistry {
description: "Allow all participants to read participant registry"
participant: "org.hyperledger.composer.system.Participant"
operation: READ
resource: "org.hyperledger.composer.system.ParticipantRegistry"
action: ALLOW
}
rule CommonReadAssetRegistry {
description: "Allow all participants to read asset registry"
participant: "org.hyperledger.composer.system.Participant"
operation: READ
resource: "org.hyperledger.composer.system.AssetRegistry"
action: ALLOW
}
rule CommonReadNetwork {
description: "Allow all participants to read network"
participant: "org.hyperledger.composer.system.Participant"
operation: READ
resource: "org.hyperledger.composer.system.Network"
action: ALLOW
}
/* Trader */
rule TraderManageClient {
description: "Allow traders to read all clients"
participant: "org.acme.mynetwork.Trader"
operation: ALL
resource: "org.acme.mynetwork.Client"
action: ALLOW
}
rule TraderManageOwnTrades {
description: "Allow traders to manage their trades"
participant(t): "org.acme.mynetwork.Trader"
operation: ALL
resource(tt): "org.acme.mynetwork.Trade"
condition: (tt.trader.getIdentifier() == t.getIdentifier())
action: ALLOW
}
rule TraderManageLots {
description: "Allow traders to read and create lots"
participant: "org.acme.mynetwork.Trader"
operation: READ, CREATE
resource: "org.acme.mynetwork.Lot"
action: ALLOW
}
rule TraderUpdateLots {
description: "Allow traders to update lots via Trade transaction"
participant: "org.acme.mynetwork.Trader"
operation: UPDATE
resource: "org.acme.mynetwork.Lot"
transaction: "org.acme.mynetwork.Trade"
action: ALLOW
}
rule TraderReadOwnTrader {
description: "Allow traders to read their own info"
participant(t): "org.acme.mynetwork.Trader"
operation: READ
resource(tt): "org.acme.mynetwork.Trader"
condition: (tt.getIdentifier() == t.getIdentifier())
action: ALLOW
}
rule TraderAddAsset {
description: "Allow traders to add assets to registry"
participant: "org.acme.mynetwork.Trader"
operation: CREATE
resource: "org.hyperledger.composer.system.AddAsset"
action: ALLOW
}
rule TraderCreateHistorianRecord {
description: "Allow traders to create historian record"
participant: "org.acme.mynetwork.Trader"
operation: CREATE
resource: "org.hyperledger.composer.system.HistorianRecord"
action: ALLOW
}
rule TraderReadOwnHistorianRecord {
description: "Allow traders to read their own historian record"
participant(t): "org.acme.mynetwork.Trader"
operation: READ
resource(hr): "org.hyperledger.composer.system.HistorianRecord"
condition: (hr.transactionType == "org.acme.mynetwork.Trade" && hr.participantInvoking.getIdentifier() == t.getIdentifier())
action: ALLOW
}
/* Client */
rule ClientReadOwnTrades {
description: "Allow clients to view their trades"
participant(c): "org.acme.mynetwork.Client"
operation: READ
resource(t): "org.acme.mynetwork.Trade"
condition: (t.client.getIdentifier() == c.getIdentifier())
action: ALLOW
}
rule ClientReadOwnEvents {
description: "Allow clients to subscribe to NewTrade events"
participant(c): "org.acme.mynetwork.Client"
operation: READ
resource(e): "org.acme.mynetwork.NewTradeEvent"
condition: (e.lot.owner.getIdentifier() == c.getIdentifier())
action: ALLOW
}
rule ClientReadOwnLots {
description: "Allow clients to view lots they own"
participant(c): "org.acme.mynetwork.Client"
operation: READ
resource(s): "org.acme.mynetwork.Lot"
condition: (s.owner.getIdentifier() == c.getIdentifier())
action: ALLOW
}
rule ClientReadOwnClient {
description: "Allow clients to view their info"
participant(c): "org.acme.mynetwork.Client"
operation: READ
resource(cc): "org.acme.mynetwork.Client"
condition: (cc.getIdentifier() == c.getIdentifier())
action: ALLOW
}
rule ClientReadTraders {
description: "Allow clients to view traders"
participant: "org.acme.mynetwork.Client"
operation: READ
resource: "org.acme.mynetwork.Trader"
action: ALLOW
}
rule ClientReadHistorianRecord {
description: "Allow clients to view their historian records"
participant(c): "org.acme.mynetwork.Client"
operation: READ
resource(hr): "org.hyperledger.composer.system.HistorianRecord"
condition: (hr.transactionType == "org.acme.mynetwork.Trade" && hr.transactionInvoked.client.getIdentifier() == c.getIdentifier())
action: ALLOW
}
/* Custodian */
rule CustodianReadAllTrades {
description: "Allow custodian to view all trades"
participant: "org.acme.mynetwork.Custodian"
operation: READ
resource: "org.acme.mynetwork.Trade"
action: ALLOW
}
rule CustodianReadAllLots {
description: "Allow custodian to view all lots"
participant: "org.acme.mynetwork.Custodian"
operation: READ
resource: "org.acme.mynetwork.Lot"
action: ALLOW
}
rule CustodianReadAllClients {
description: "Allow custodian to view all clients"
participant: "org.acme.mynetwork.Custodian"
operation: READ
resource: "org.acme.mynetwork.Client"
action: ALLOW
}
rule CustodianReadAllEvents {
description: "Allow custodian to subscribe to NewTrade events"
participant: "org.acme.mynetwork.Custodian"
operation: READ
resource: "org.acme.mynetwork.NewTradeEvent"
action: ALLOW
}
rule CustodianReadAllTraders {
description: "Allow custodian to view all traders"
participant: "org.acme.mynetwork.Custodian"
operation: READ
resource: "org.acme.mynetwork.Trader"
action: ALLOW
}
rule CustodianReadOwnCustodian {
description: "Allow custodian to view their info"
participant(c): "org.acme.mynetwork.Custodian"
operation: READ
resource(cc): "org.acme.mynetwork.Custodian"
condition: (cc.getIdentifier() == c.getIdentifier())
action: ALLOW
}
rule CustodianReadHistorianRecord {
description: "Allow custodian to view all trade historian records"
participant(t): "org.acme.mynetwork.Custodian"
operation: READ
resource(hr): "org.hyperledger.composer.system.HistorianRecord"
condition: (hr.transactionType == "org.acme.mynetwork.Trade")
action: ALLOW
}

After all steps are completed, we can deploy the application by clicking Update on the left.

How to test in Playground

Now that we have the complete definition of our application, we can test it using the Composer Playground Test module (click Test at the top).

1. Create participants

First, let’s seed our application with some participants. We can create 2 clients: a custodian and a trader.

2. Create identities for each participant

In order to be able to interact with the business network a participant needs an associated identity. Identities can be created in the ID registry (open the user dropdown on the top right). We’ll create an identity for each participant by specifying its name and pointing to an instance of the participant created in the previous step. At a minimum, we need 4 identities: 2 clients, 1 trader, and 1 custodian.

3. Login as Trader

We can now login using the newly created identities by clicking “Use now” next to each identity in the list. First, we login as a trader and create a new Lot that doesn’t belong to any client (client ID is null).

Then, we assign the ownership of this lot to client 1 via a Trade transaction.

Now, let’s create one more lot and assign it to client 2 via another Trade.

4. Login as Client

Then, we’ll login as client 1 and client 2 to check if we can see our Lots.

Client 1 lots
Client 2 lots

As you can see, each client sees only lots that they have ownership of and they are not aware of trades for other clients.

5. Login as Custodian

Let’s login as a custodian to check that we can view all trades. Indeed, both trades are visible, as you can see below.

Custodian can also view history of all trades:

Note on decentralization

Keep in mind that when a transaction is broadcasted to the Hyperledger Fabric network, each configured peer is independently executing the custom transaction processing logic which ensures the correctness of the blockchain update. That’s a major architectural difference and — in many cases — an advantage over the traditional centralized, client-server approach. In the traditional approach a single organization would be responsible for managing the data and exposing it to other participants, forcing others to trust it and making it a potential single point of failure.

In the blockchain approach, each peer is responsible for managing and reconciling their own copy of the data. This makes the entire network more resilient and decentralized. However, it is not completely simple to configure. There are additional privacy implications to consider since the data is now hosted in many places which creates a potentially larger threat surface.

Conclusion

Hopefully this has given you a better understanding of what Hyperledger Composer is and how it can be useful in sharing information and keeping track of assets and other concepts among multiple participants. Feel free to export the .bna file of your network and follow the tutorial here to deploy the application to a local Hyperledger Fabric network. We’d love to hear your thoughts on Hyperledger Composer. Please, leave any questions or comments below.

More by Andrei Anisimov

Topics of interest

More Related Stories