Using Patterns in Coding Blockchain Smart Contracts
Mar 26, 2018
Blockchain is a tremendously promising technology. We have been applying blockchain technology to transform various business processes especially supply chain problems enabling it to become more efficient both financially and operationally. We at IT People have been involved with the blockchain journey since late 2015. Throughout 2016 until now, our team has built solutions in both Hyperledger-Fabric (an IBM Blockchain innovation from the Linux Foundation) as well as Private Ethereum. Along the way, as we architected applications, we learnt and experimented in developing smart contracts and noted that patterns can be applied to design.
Last August, I had a linkedin post on Scaling Blockchain Solutions. This post tries to explore one of the bullets in that post. We discuss some of the patterns here in this initial attempt at stirring some conversation.
Patterns for Contract Development
Patterns help break the solution into simple services and thus enable simplicity, maintainability and scalability. We have developed and tested multiple solutions in private Ethereum networks based on the Parity client, and many supply chain projects on Hyperledger-Fabric.
The following patterns described below were first discovered while we were implementing Ethereum Solutions. Subsequently, we refactored the solutions we built on Hyperledger Fabric, where most of the earlier solutions were based on a single monolithic contract with several functions processing business transactions and events. We broke up our back-end solution into smaller loosely couple contracts. Some of the contracts were based on “asset type” which refers to assets as anything that has value and can be moved between counter parties. Assets include crypto-money, financial securities, documents, parts, items, votes etc. We list few of the patterns which have their roots in many of the earlier discussions in SOA, J2EE, EAI etc.
The Registry Smart Contract
Motivation: During the building of the solution, once a smart contract is deployed, it is immutable and runs for ever. Newer versions of the contract are deployed over time, and unless the old one is destroyed, the multiple versions run in parallel. How does the application know which one of the contracts is legal or which version to invoke for a particular client.
Solution: A singleton instance of a special smart contract called the “Registry Contract” can be defined which acts as a registry for all deployed smart contracts. Every time a smart contract is deployed, a transaction is invoked on this contract to register the details of the newly deployed contract and any other updates to previous versions. This pattern is applicable to both Ethereum and Hyperledger-Fabric solutions. While contract account number is the key in Ethereum, the Chaincode ID can be used in Fabric.
The Dynamic Instantiation of Contracts
Motivation: There are situations where several contracts may belong to the same family by sharing some common attributes and behavior and based off of a common template. These contracts may be organization specific and managed using user privileges assigned by the organization. Such contracts may be created at runtime. For example, if a buyer deals with several suppliers, the contract between the buyer and each supplier may be a separate smart contract.
Solution: Maintain contract templates. Instantiate a contract from the template as per the workflow at runtime. For example if an organization deals with multiple suppliers, then each supplier may have an independent contract with the buyer. In the case of Ethereum/Solidity, any contract can instantiate another contract from within the blockchain. In the case of Hyperledger Fabric, a new instance of the contract has to be deployed by the client from the outside into the blockchain network (e.g. from the NodeSDK Client via a deploy call).
The Lookup Smart Contract:
Motivation: If there are a number of run-time instantiated smart contracts, there should be a method to lookup the appropriate contract using key attributes and then send transactions to that specific contract. For example, if the incoming transaction refers to “Supplier_01”, then, there should be a method to retrieve the contract associated with that supplier identifier and then send transactions such as “UpdateSupplierRating(“Supplier_01”, args …) “ to that contract.
Solution: We can solve this using the same concept as a “Registry Contract”. We utilize a “Lookup Contract” that maintains a map of all contracts, in our case “Supplier Contracts” keyed by supplier ID. When an incoming request is received, the contract retrieves the appropriate supplier contract based on the “supplier Id”. The Lookup contract acts as a registry of all contract addresses or URIs for the suppliers as depicted in the diagram below. This pattern is implementable in both Ethereum/Solidity and Hyperledger Fabric. In Hyperledger Fabric, the contract address is the chaincode name or identifier.
The Verifier Contract Pattern:
Motivation: When a transaction comes into the blockchain, it is necessary to verify the credibility of the user initiating the transaction and whether the user has the privileges to execute the transaction on that contract.
Solution: We implement a “Account Manager” that maintains records of all legal accounts and some additional information and a “Role Manager” that maintains maps of roles to functions that can be accessed. The “Access Verifier” acts as an internal check and verifies every transaction before execution against the account making the request and whether the account has permissions to run the transaction. An account may belong to an application, an end user persona, a contract or a client. We applied this approach while designing a private Ethereum network until we could build on top, an identity verification service and a certificate management service. This design pattern can be implemented in Fabric as separate chaincodes and using the chaincode-to-chaincode calling API.
The Workflow Contract Pattern
Motivation: Almost all solution logic has some work-flow consisting of input, processing and output. A workflow is a series of steps or tasks performed to achieve results. The workflow may have several branches triggered by conditions. In our case, workflows do not store any data hence they are stateless.
Solution: We implement a smart contract that executes a sequence of steps based on the incoming payload or event. Each step may execute minimal logic such as validate incoming data or invoke another smart contract. In Ethereum/Solidity, our workflow does not have any ledger state variables but can make calls to other smart contracts to read data. In Hyperledger-Fabric, we have no calls to “putState” but the logic may contain “getState” to retrieve some data from the ledger, transaction-history or world-state by calling another chain code.
The Business and Data Contract Pattern:
Motivation: The ledger is immutable, and data recorded in the ledger cannot be altered and manipulated. In blockchain solutions, data is owned by the smart contract and any reads or writes to the ledger can be performed only via the APIs provided by the smart contract. Thus, the underlying ledger is not directly accessible. The issue arises when a new version of the contract must be deployed. Data from the previous version of the contract must be migrated to the new version.
Solution: Split the smart contracts such that the business logic is isolated into the “Business Contract” and the recording of the data into the ledger is performed by the “Data Contract”. There is one caveat – we assume that the data contract will not change that often, and that it accommodates for most commonly used data attributes and operations.
The Routing Pattern
Motivation: As seen in the “Registry Pattern” and “Lookup Pattern”, contracts can be instantiated dynamically at run-time and their contracts addresses are assigned during that phase. An incoming request from a client knows the function to be executed against a contract but does not know the address of the contract. In other situations, the workflow contract must be able to route transactions to the appropriate contract based on workflow.
Solution: Use a “Router” to receive incoming requests, perform a lookup to retrieve contract or chaincode address and route the request to the appropriate contract. The “Router” can maintain internal routing table or it could work in conjunction with a “Registry” or “Lookup” contract.
The On-Chain Off-Chain Pattern
Motivation: In almost all enterprise applications, we deal with business objects such as order, supplier, customer and product. Storing large data objects in any blockchain technology in the ledger has implications. The storage requirement is enormous and is replicated across all the nodes. Queries against the object or a list of objects can result in latency issues. Finally, there is a cost for storage and data transfers. Data stored on the blockchain must conform to blockchain principles and requirements for decentralization, trust, transparency yet support privacy, auditability and immutability. However, business solutions represent end-to-end applications to solve business problems.
Solution: Our approach is to store blockchain specific attributes of a business object on the chain, while storing all other attributes about the object that support the solution for transaction processing and queries in an off-chain database such as MongoDB or CouchDB. This approach has helped us to engineer solutions to meet performance requirements and isolate private information to the off-chain storage with their hashes stored on-chain. The off-chain data store is attached to the client interface and complements the on-chain datastore. The combination of the two is considered part of the blockchain aspects of the overall solution.