Implementing a simple Smart Contract for Asset Tracking [blockcentric #6]

No Comments

blockcentric

Our article series “blockcentric” discusses blockchain-related technology, projects, organization and business concerns. It contains knowledge and findings from our work, but also news from the area.
Blockchain in the supply chain is covered in several parts:
Part 1 (General introduction)
Part 2 (Supply Chain platform)
Part 3 (Implementation on the shop floor)
This article (Smart Contracts for asset tracking)
Part 5 (Deploying Smart Contracts on the blockchain)
You can find an implementation on our demo site: https://asset-tracker.codecentric.de/

A simplified asset tracking case

In our simplified case we would like to track assets throughout the supply chain. In reality, the data structures for the assets might be more complicated, but we will focus on a few aspects only: name, description and manufacturer. It should be guaranteed that there is only a single instance of an asset at any time which leads to a uniqueness requirement of the ID. We would like to keep track of origin and history of the asset. The key actions that should be supported are the creation of new assets as well as transfers of assets. Furthermore, there should be a possibility to check whether a person is actually in the possession of the asset ownership certificate or not.

The implementation environment

As an easy starting point to implement smart contracts, we use the Ethereum blockchain. The key feature of Ethereum is the ability to execute programs on the nodes of the blockchain. It can be seen as an application platform that runs contracts as unstoppable programs on the blockchain. Ethereum has its own virtual machine (Ethereum virtual machine) that executes programs on all nodes the same way. The programming language that defines these programs is called Solidity, which is a object contract-oriented, high-level language for implementing smart contracts. Solidity supports inheritance, libraries, complex types and reads quite similarly to object-oriented programming languages, such as C++, Python or JavaScript.

Once you have implemented your smart contract functionality, you are ready to either test the contract locally, by deploying it to a test blockchain such as truffle-ganache or to an Ethereum test network such as the Rinkeby test network. Both options will be covered by follow-up articles. The interaction with the contract is done by using either command-line tools such as geth or via web frameworks such as web3.js. To manage your wallets in your browser, you would need a wallet store such as Metamask that connects to either your local or a public test chain as well as the Ethereum network.

Structure of the smart contract

Let’s have a look a the basic structure of a smart contract written in Solidity.

pragma solidity ^0.4.2;
import "assets.sol";
 
contract AssetTracker {
    string id;
 
    function setId(string serial) public {
          id = serial;
    }
 
    function getId() public constant returns (string) {
          return id;
    }
}

The contract file opens with a section on the applicable Solidity language version. Pragmas are known from compilers such as gcc. They give the compiler additional information on how to process the given code. This given pragma tells the compiler to use the ‘^’ highest available language version above ‘0.4.2’.

Furthermore, you are able to import code from other files at the global scope level by using the well-known ‘import’ statement, just as in most other languages.

In this example we have added some public getter and setter functions that can be used both as an internal and as an external function. Beyond this, there is the possibility to create events along the way that can be used to log processes or states. We will make use of the events in order to keep track of supply chain events such as goods receipt or asset creation.

A brief excursion to types and costs

The static types that are available, such as string, int, bool …, will come with the typical unary and binary operators. You can find a full list of types here. You shouldn’t be lazy about learning and using the types properly, because this will have impact on the running cost oft your transactions. The transaction effort, described in the unit gas, will depend on the operations executed and the data stored. This effort is priced by you. If you pay more for the execution of your code, your transaction will be preferred by the network, hence executed sooner.

If you want to drill down into the economics of transactions, you should have a look at ‘Calculating Cost in Ethereum Contracts‘ and ‘Ethgasstation‘ to get a feeling about the associated costs with your smart contract. The details of gas cost for opcodes, e.g. the formerly used SSTORE opcode, can be found in the Ethereum yellow paper. Another way to have a look at the cost is to use the online Solidity compiler that allows you to have a look at the Ethereum Virtual machine code  generated from your contract. It estimates the gas price and reveals the inner workings of how the different data types are handled on the stack.

Tracking data structure

The following  struct describes our simplified asset.

struct Asset {
    string name;
    string description;
    string manufacturer;
    bool initialized;    
}

We use members such as describing properties such as name, description and process control variables such as initialized and manufacturer. They are used to check whether this asset was already manufactured and who the manufacturer is.

In order to store the assets, we create two mappings that will allow us to store asset properties as well as the relation between assets and wallets based on asset uuids.

mapping(string  => Asset) private assetStore;

is used later on to store assets under their respective uuid:

assetStore[uuid] = Asset(name, description, true, manufacturer);

For the wallet store, we use the following mapping:

mapping(address => mapping(string => bool)) private walletStore;

is used later on to make the assignment of an asset to a wallet

walletStore[msg.sender][uuid] = true;

Declaring the events

For different real-world events in the supply chain, such as asset creation or asset transfer, we define counterparts in the smart contract.

event AssetCreate(address account, string uuid, string manufacturer);
event RejectCreate(address account, string uuid, string message);
event AssetTransfer(address from, address to, string uuid);
event RejectTransfer(address from, address to, string uuid, string message);

Declaring the functions

The first function that we would need is the create asset function. It takes all information needed to specify the asset and checks if the asset already exists. In this case we trigger a formerly declared event – RejectCreate(). If we have a new asset at hand, we store the data in the asset store and create the relation between the message sender’s wallet and the asset uuid.

function createAsset(string name, string description, string uuid, string manufacturer) {
 
    if(assetStore[uuid].initialized) {
        RejectCreate(msg.sender, uuid, "Asset with this UUID already exists.");
        return;
      }
 
      assetStore[uuid] = Asset(name, description, true, manufacturer);
      walletStore[msg.sender][uuid] = true;
      AssetCreate(msg.sender, uuid, manufacturer);
}

In order to transfer the asset, we create a function that takes the address of the target wallet along with the asset id. We check two pre-conditions: The asset is actually existing and the transaction initiator is actually in possession of the asset.

function transferAsset(address to, string uuid) {
 
    if(!assetStore[uuid].initialized) {
        RejectTransfer(msg.sender, to, uuid, "No asset with this UUID exists");
        return;
    }
 
    if(!walletStore[msg.sender][uuid]) {
        RejectTransfer(msg.sender, to, uuid, "Sender does not own this asset.");
        return;
    }
 
    walletStore[msg.sender][uuid] = false;
    walletStore[to][uuid] = true;
    AssetTransfer(msg.sender, to, uuid);
}

We would also like to have access to the asset properties by just giving the uuid. Since it is currently not possible to return structs in Solidity, we return a list of strings.

function getAssetByUUID(string uuid) constant returns (string, string, string) {
 
    return (assetStore[uuid].name, assetStore[uuid].description, assetStore[uuid].manufacturer);
 
}

Furthermore, we would like to have a simple way to prove the ownership of an asset without the need to fiddle around the transaction log. So we create a helper function isOwnerOf().

function isOwnerOf(address owner, string uuid) constant returns (bool) {
 
    if(walletStore[owner][uuid]) {
        return true;
    }
 
    return false;
}

Once the contract is deployed, we can interface with the smart contract by using web3.js. The following example is an excerpt for creating a new asset.

export const createAssetInContract = async (assetData, publicKey) =>; {
    console.log('Creating asset...');
    const atContract = await AssetTracker.deployed();
    const asset = atContract.createAsset(
      assetData.name,
      assetData.description,
      assetData.assetId,
      assetData.manufacturer,
      { from: publicKey },
    );
 
    return asset;
};

This simple example shows a basic asset tracking functionality in the blockchain. There are many ways to improve the design and the functionality, such as on-chain uuid generation. If you want to discuss tracking solutions in the blockchain, feel free to start a discussion.

Part 1 (General introduction)
Part 2 (Supply Chain platform)
Part 3 (Implementation on the shop floor)
This article (Smart Contracts for asset tracking)
Part 5 (Deploying Smart Contracts on the blockchain)
You can find an implementation on our demo site: https://asset-tracker.codecentric.de/

If you are interested in this topic, please let us know. We would like to hear your comments and amendments. Feel free to drop me a few lines: kai.herings@codecentric.de and follow me on Twitter: https://twitter.com/kherings

Previously published blockcentric posts

 

Comment

Your email address will not be published. Required fields are marked *