November 20, 2020
Nowadays there are 2 buzz words that are getting lot of attention for several different causes, these are Smart Contracts and Blockchain. If these are new to you I recommend you to read our Basic Concepts article before moving forward.
The last couple of years, here at SOUTHWORKS, we have been gaining lot of experience working in the Retail world with some giants of the industry (you can see some related articles here , here and here ). As side projects thinking how can we improve our customers’ offerings, and by transitive relationship their final customers’ experiences, we are always trying out different things while getting our hands ‘dirty’ deep-diving into new technologies that could bring the innovation leap these big players need to adopt the latest trends sooner than their competition.
In this case, we decided to start by standing at the very basis of the Retail industry which initiates at the supply chain. This scenario provides several steps where interactions (transactions) between buyers and suppliers take place constantly, so it is a perfect place where blockchain concepts can be applied.
Finally, and during our research, we found out which other real-world applications are out there, aside from investing in cryptocurrencies, that involve blockchain and smart contracts.
Here are some other examples worth mentioning:
But as I said this article focus on the actors and interactions of a supply-chain.
Before we get into the thing, I don’t want to skip mentioning the great team of software engineers that contributed and provided feedback into putting this together: Ignacio Daniel Boada, Lucas Daniel Carballo, Pablo Costantini, Ignacio Agustín Grayeb & Mara Greene - thank you guys, you made it happen!
We hope that you like it 😃
In this article, we present and describe our chosen supply chain scenario (we focused on operations between a consumer and a retailer), explain the architecture including the technologies we used to implement the blockchain network and smart contracts and provide references to our reference implementation. We also include an appendix where we discuss other approaches and recommendations surrounding our implementation.
So.. let’s get started..
To put what we learned about blockchain into practice, we came up with a use case that applies to a consortium network. Given that consortium members usually correspond to different organizations, we thought of a simple structure for a supply chain.
Our supply chain has four actors:
In this scenario, transactions will always occur between a seller and a buyer. The actors will take the seller or buyer’s role depending on which step we are looking at. For example, in a transaction between the producer and the distributor, the former will be the seller, while the latter will be the buyer. The same will happen between the retailer and the consumer. For simplicity, we will focus on this last business relationship.
We implemented two smart contracts for this use case:
Although a single contract could have been used, we decided to create two to maintain separation of concerns.
The image below shows the workflow that would occur when a consumer places an order and the role each smart contract will play. The operations will be executed via the Retailer smart contract, which in turn will call the Stock smart contract to get certain data. Some operations will be accessible to the consumer, while some will be restricted to the retailer. Also, events will be emitted when each relevant operation is completed successfully.
This way, the consumer will be able to create an order with available items, and the retailer will be able to confirm or reject the order and deliver it if it applies.
If an operation fails (e.g. an item in an order has no stock), the transaction is reverted, which means all the state changes it produced are dismissed. However, the application will return a message explaining what went wrong.
We implemented our solution on Azure, using different resources to build the complete workflow. All of these resources were deployed on our Azure AD subscription.
We implemented the two smart contracts (Retailer and Stock) on Solidity and deployed them to an Azure Blockchain Service consortium network. These contracts emit events to notify and provide relevant information about the operations that take place within them. To interact with these contracts and provide entry points for the whole system, we created Azure Functions that allow users to call contract functions using any REST API client. We used Azure Blockchain Data Manager together with an Event Grid Topic to capture events emitted from the contracts and send them as notification emails and as entries in a Cosmos DB instance.
Azure Blockchain Service provides a set of tools that simplify the creation and management of consortium blockchain networks, which allows developers to focus on the business logic of their deployed apps, rather than the underlying infrastructure.
Azure Blockchain Service has built-in consortium management. In this context, a consortium is a logical group used to manage the governance and connectivity between blockchain members who transact in a multi-party process. Pre-defined smart contracts determine the actions that a consortium member can take. The administrator can invite other parties to join the consortium.
Azure Blockchain Service resources are isolated in a private virtual network. Transaction and validation nodes are virtual machines (VMs) that can’t communicate with other VMs in a different network. This ensures communication remains private within the virtual network.
Transactions can be sent to blockchain nodes via an JSON-RPC endpoint. The JSON-RPC endpoint includes the provider member as well as an access key. JSON-RPC is a type of RPC protocol which uses JSON to encode requests and responses between client and server. The JSON-RPC interface uses Ethereum JSON-RPC Protocol with Web3 authentication. The JSON-RPC calls can be done over HTTP or WebSockets (See documentation ). Clients communicate with a transaction node using a reverse proxy server that handles user authentication and encrypts data over TLS (see Azure’s official documentation for more details). The reverse proxy is getting the RPC and then looking up the authentication information using the OAuth Protocol. The Reverse Proxy uses TLS Protocol to communicate with the transaction node.
The image below shows a consortium with three members in the form of a Venn diagram. Within the network, data is visible to all participating nodes. However, the Quorum protocol provides a way to keep certain transactions’ contents private only to those members who take part in it (see the ‘Private transactions in Ethereum’ section from our Basic Concepts post).
In this sample consortium, transactions will follow this path:
Azure Blockchain Data Manager allows loading blockchain applications to capture, transform and deliver transaction data to Azure Event Grid Topics , which are endpoints where events can be sent. There are built-in topics, but users can also create custom ones. When a custom topic is created, an Event Grid API endpoint is created and associated to it. Azure Blockchain Data Manager internally performs an HTTPS POST request to this endpoint to send events to it. For more details, see ‘Capturing and viewing events’ section below.
We used this tool to capture events from our smart contracts and consume them. Azure also provides a way to connect Blockchain Data Manager with a Cosmos DB instance, which we explain later in this post.
In this section we explain how each Azure component fits into the action flow from the consumer’s perspective.
As we mentioned in the ‘Overview’ section, we implemented two smart contracts: Stock and Retailer. The former keeps the state of the items in stock, while the latter provides functions to create, cancel, confirm and deliver orders. In the following sections we dig deeper into the specifics of each of these contracts.
In these next sections we deal with an important concept: contract ownership. Functions from a smart contract can be restricted to certain addresses, which can correspond to users’ wallets or to other contracts. In Solidity, there is a field called msg.sender that returns the address that calls a specific function. This can be used to determine whether an address (a user or another contract) may or may not call a function within the contract.
OpenZeppelin provides contracts to handle ownership using modifiers that can be applied to contract functions to restrict access to certain addresses. These contracts allow to have a contract owner or to keep a whitelist of addresses that are entitled to operate the contract. They also allow to transfer ownership.
This contract holds the supply chain’s inventory in the form of a collection of stored items together with their basic info and available stock. Whitelisted addresses can only operate this contract.
In the basic scenario, with a retailer and a consumer, the retailer and the Retailer contract must be added to the whitelist. This will allow the retailer to add and remove items and update their info and stock. It will also allow an item’s stock to be decreased once an order is accepted, through the contract.
On the other hand, the consumer will not be whitelisted and will only be able to view items and their stock (which are public operations in our scenario).
To implement the whitelist, we used OpenZeppelin’s WhitelistedRole contract.
In the following subsections, we explain this contract’s implementation.
Items are organized as a Solidity mapping, which allows to index elements in a key-value format. This mapping is called items, where the key is an unsigned integer value representing its id and the value is a struct called item. This struct contains the following fields: name, description, category (strings) and stock (integer).
The items mapping uses 32-bit unsigned integers as keys (item IDs), which means there will be a maximum of 4,294,967,296 (2³²) available keys. We consider this number is more than enough for our application, so we don’t need to use larger integers as our item IDs. On the other hand, 16-bit integers might have been a bit limited.
We implemented the following functions for this contract:
Events are emitted from some of the functions, returning messages as arguments for logging purposes and/or obtaining relevant values.
This contract contains the logic that allows orders to be created and delivered and relies on the Stock contract to query, select, and decrease items stock.
As every contract it has an owner, who in the consumer-retailer scenario, is the retailer. Some functions are accessible to the consumer to perform operations such as creating an order, canceling an order, and confirming a delivery. Others are restricted to the owner, for instance, accepting, rejecting, and delivering an order. OpenZeppelin ‘s Ownable contract is used to accomplish this. This smart contract provides a modifier called onlyOwner that can be added to specific functions in a contract, which only allows the contract owner to call them (the owner is the address that deploys the contract by default, but this can be changed or ownership can be transferred at any point).
In the following subsections, we give details about the implementation.
Orders are organized in a mapping that indexes them by ID (unsigned integer). Each order is a struct that contains three fields: customerAddress, customerEmail, status and items. The last field is another mapping that holds the selected items, each of which is a struct that contains itemId and itemQuantity.
Below is a diagram showing the different states that an order can be in:
This contract contains the functions listed below. Some functions are restricted to the contract’s owner, which is done with OpenZeppelin’s Ownable contract, using the onlyOwner modifier. Others require that the customer who placed the order is the caller. When an order is created, the user’s Ethereum address is stored in the order struct as the customerAddress field. This is achieved by assigning msg.sender (the address of the function’s caller) to this field. When attempting to cancel an order, comparing msg.sender to the customerAddress field that was stored in the order will determine if the caller is in fact the same user who created it.
The following events are emitted from the contract. All of them provide a message and relevant data in their arguments.
Azure Functions is a serverless compute service that allows the user to run event-triggered code without having to explicitly provision or manage its infrastructure.
You can leverage the Solidity extension for Visual Studio Code (follow this tutorial) to generate C# API classes for each contract, which were then consumed by our Azure Functions.
For each contract, the extension automatically generates two C# files: the contract definition and service. These allow developers to interact with the smart contracts from C# applications. In this case, we used them to develop the code for our Azure Functions. Here you can view the Azure Functions to interact with both the stock and retailer contracts. The image below shows the project’s folder structure, highlighting the auto-generated files and the ones that correspond to the Azure Functions.
As we explained, both contracts emit events when certain operations are executed successfully. Azure provides tools to capture these events and process them to be consumed in different ways. We chose to store them in a Cosmos DB instance and send email notifications.
To accomplish this, we had to set up several things, which we explain in the following sections.
The steps to configure Blockchain Data Manager are explained in detail in the official documentation . However, here we provide a summary.
4. Finally, by triggering events from our application, we could see them in the deployed web app. The event shown in the image below corresponds to an address added to the whitelist in the Stock contract.
Azure provides tools to create a Cosmos DB instance and to connect it to Blockchain Data Manager. This tutorial explains the steps to send data from Blockchain Data Manager to Cosmos DB. The first few steps are how to configure Blockchain Data Manager and add an application, which we already explained. The following steps are:
With this setup, we could capture events from our applications and view the data in an ordered manner.
There are two kinds of functions in a Solidity smart contract: calls and transactions. A call is a function that does not change the state of the blockchain, while a transaction does. For example, a function that just reads a value stored in the contract is a call, while one that updates its value is a transaction.
Any function in Solidity can have a return value. However, in Nethereum, only calls allow an application that uses the contract to access this value. As no state changes occur and, hence, no transactions, the return value is immediately available. Transactions, on the other hand, return a receipt, which contains details about the transaction, but not its return value. Transactions must be verified by the blockchain’s consensus mechanism, which makes the value not immediately available. See the Nethereum documentation for more information.
As a workaround to get the function’s return value from within a transaction, we can publish the function’s return value in an event. By doing this, the return value can be made available to any application that is listening to the event.
To run our solution, you will need to clone our repository here.
Then, you will need an Azure subscription (a free trial is available).
Finally, you will need an IDE. We used VS Code as our main one, although we also used Remix to test some things. Remix is an online IDE, which is very convenient for running quick tests.
Once in VS Code, we used the following required extensions:
If you use Remix to run smart contracts or to interact with them, keep in mind that you will need a wallet app. We used metamask to connect with the blockchain.
To check API payloads and available endpoints there is documentation for the Function Apps endpoints:
Another approach could be using Logic Apps to act as entry points for our contracts. These allow calling contract functions through REST API requests.
Using the Logic Apps builder, the developer must specify input parameters and the smart contract functions that must be called. To reference the contract’s instance, the user must provide the contract’s ABI (Basic Concepts [⁸]) and bytecode (a hex code that includes the constructor logic and parameters of the smart contract, also known as constructor bytecode or creation bytecode). For all functions except contract deployment, the contract’s address must be provided as a query parameter in the endpoint’s URL.
We chose the Azure Functions approach over this one because it provides more capabilities. Moreover, the Ethereum Connector used in Logic Apps was deprecated last August.
When creating Azure Functions, we recommend using the Solidity extension . The Azure Blockchain Ethereum Extension presents issues with the uint32 type, which is used in some of our contract functions and their associated endpoints.
To identify different customers in our distributed app, you can use our Consumer API, where creating a customer profile is as simple as running an Azure Function and it will be stored in a database with its name, email, id and key. This will create a wallet that will be associated to the customer.
To authenticate a contract (A) into another contract (B) we recommend using WhitelistedRole access control. It allows you to whitelist contract A and anyone who needs access. With this approach, there is no need to add anything into contract A for authenticating. Plus, the usage is like Ownable’s access control.
Originally published by Mauro Krikorian for SOUTHWORKS on Medium 20 November 2020