Smart Contracts in the Retail Industry

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:

  • Real estate applications
  • Money transfer use cases
  • IoT use cases
  • Music royalties tracking
  • Personal identity security
  • Voting mechanisms
  • Secure sharing of medical data

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 😃

Our scenario

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..

Overview

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:

  1. Producer: The source of the chain’s supplies.
  2. Distributor: The member that holds different supplies from the producer with overstock to provide them to different retailers.
  3. Retailer: This actor will have less stock than the distributor but will sell without conditions to the customer.
  4. Customer or “consumer” (interchangeably throughout this article): As the end of the chain, they will create orders to be delivered to them.

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:

  1. Stock: This contract stores all items with their info and available stock. The seller (typically, the retailer) will be able to add, remove, and update items and their stock, while the buyer (consumer) will only be able to read them.
  2. Retailer: This one contains the logic of the workflow. As such, it will have the capability to create, accept, cancel, and deliver orders. This contract will have to operate the Stock contract to determine if an order is valid (i.e. all requested items have stock) and to update its items’ stock, should it be accepted.

Although a single contract could have been used, we decided to create two to maintain separation of concerns.

Buy supplies: the retailer-consumer scenario

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.

Our solution

Overview

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

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:

  1. A new RPC-JSON transactions comes from a client, the entry point of each member will be this reverse proxy.
  2. The authorized transaction pass through the proxy to the Transaction Node.
  3. The Transaction Node sent it to the Validator Node.
  4. The Validator Node broadcast the transaction between all the Validators. Then it is queue to be validated.

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.

Consumer action flow

In this section we explain how each Azure component fits into the action flow from the consumer’s perspective.

  1. The consumer creates a customer profile through the Consumer API which is identified by a unique user id which is returned.
  2. The consumer creates an order through the API using the user id from the created user profile.
  3. Internally, the corresponding endpoint creates an JSON-RPC transaction with the transaction node of the consortium member where the contracts are deployed.
  4. The createOrder function from the Retailer contract is called, which generates a transaction in the blockchain.
  5. Blockchain Data Manager captures events from the transaction node and sends them to the Event Grid Topic.
  6. A Logic App , consortium-events, triggers when the Event Grid receives events and sends them as notification events, while also storing them in a Cosmos DB instance.

Smart contracts

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.

The Stock smart contract

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.

Stock contract data structures

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.

Stock contract functions

We implemented the following functions for this contract:

  • itemExists (private: only accessible within the contract)
  • createItem (only whitelisted addresses)
  • removeItem (only whitelisted addresses)
  • increaseItemStock (only whitelisted addresses)
  • decreaseItemStock (only whitelisted addresses)
  • updateItemName (only whitelisted address)
  • updateItemDescription (only whitelisted address)
  • updateItemCategory (only whitelisted address)
  • getItemStock
  • getItem

Stock contract events

Events are emitted from some of the functions, returning messages as arguments for logging purposes and/or obtaining relevant values.

  • ItemCreated
  • ItemRemoved
  • ItemStockUpdated
  • ItemFieldUpdated

The Retailer smart contract

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.

Retailer contract data structures

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:

Retailer contract functions

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.

  • orderExists (private: only accessible within the contract)
  • createOrder
  • getOrderStatus (only the owner or the customer who placed the order)
  • checkOrderItemsAvailability (only the owner)
  • cancelOrder (only the customer who placed the order)
  • getNewOrders (only the owner)
  • acceptOrder (only the owner)
  • deliverOrder (only the owner)
  • rejectOrder (only the owner)
  • confirmDelivery (only the customer who placed the order)
  • updateMinItemsPerOrder (only the owner)
  • updateStockContract (only the owner)

Retailer contract events

The following events are emitted from the contract. All of them provide a message and relevant data in their arguments.

  • OrderCreated
  • OrderUpdated
  • OrderDelivered

Entry points: Azure Functions

Azure Functions implementation

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.

Capturing and viewing events

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.

Azure Blockchain Data Manager

The steps to configure Blockchain Data Manager are explained in detail in the official documentation . However, here we provide a summary.

  1. First, we created an Event Grid Topic. For testing purposes, we deployed a web app that Microsoft suggested in the same guide to view events. We added the app’s URL as an Event Subscription within the Event Grid Topic.
  2. Then, we created a Blockchain Data Manager instance, which points to the Event Grid Topic we created, specifying the transaction node of the corresponding consortium member.
  3. We added a blockchain application for Blockchain Data Manager to decode its events. To do so, we executed the following steps:
  • We saved the contract’s ABI and transaction bytecode (a hex code that identifies a contract that was deployed to the blockchain).
  • We created a Storage Account and uploaded the two files.
  • For each file, we generated a Shared Access Signature (SAS) URL from the portal.
  • On the Blockchain Data Manager instance, we added a new application, specifying the SAS URLs for both files.

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.

Storing events in Cosmos DB

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:

  1. Create an Azure Cosmos DB account and add a database and container.
  2. Create a Logic App to connect Event Grid to Azure Cosmos DB. We named ours consortium-events.
  3. Add an Event Grid trigger that fires when an event happens, or a condition is met. In our case, the trigger is when a resource event occurs, for which we specified our Event Grid Topic.
  4. Add a Cosmos DB action to create a document in Cosmos DB for each transaction. In this case, we used the message type as the partition key to categorize messages.

With this setup, we could capture events from our applications and view the data in an ordered manner.

Using events to get the function return value

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.

Requirements

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.

API Documentation

To check API payloads and available endpoints there is documentation for the Function Apps endpoints:

Disclaimers:

  • In our scenario, we are passing the contract address (Basic Concepts [⁷]) as a parameter in all API calls for the sake and simplicity of it. The user id is being past to link the consumer to the operation and is used to obtain the user profile. We get this contract address as part of the result of the contract deployment.
    Unfortunately, the contract address is a 42-character hexadecimal number, which must be known to make any API calls involving smart contracts. In a real scenario, the user will not need to know the contract address. The user would be able to use a more user-friendly name, for example, the company’s name, to identify the smart contract.
  • Also, for simplicity, we have not secured our APIs. For a customer to call an API endpoint, they just need to provide an ID, which is used by the backend to obtain their Ethereum credentials from a database. In a real-world scenario, the APIs will require the customer to have an authorization token obtained through a login flow. Additionally, the customer’s credentials would be stored in a safer environment, such as a key vault.
  • In our solution we stored wallet’s passwords in a CosmosDB collection just for simplicity. In a real application, those wallet passwords should be stored properly in user profile encrypted and salted at least. Remember that those passwords can’t be changed or recovered.

Appendix

A different approach: Logic Apps

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.

Solidity Extension for Azure Functions

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.

Customers’ wallets in API

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.

Authenticating Contracts Using WhitelistedRole

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