The simple chaincode

Writing chaincodes (i.e. programs that consist of smart contracts) in Hyperledger Fabric is easy. In the Node.js SDK you can extend Contract class and write an async function which will have access to the state. The simplest example of the chaincode may look as follows (all examples are written in TypeScript):

The world state in the Hyperledger Fabric is preserved in the form of key-value store. In the example above the current incrementer value is stored under the key incrementer_value . The getValue smart contract retrieves the current value. The increment smart contract increments and overwrites it.

So far so good. This example works perfectly fine… unless you call it in parallel. It simply doesn’t work in parallel. Why? Because of key collisions, a mechanism to provide state consistency in the Hyperledger Fabric. A single key incrementer_value cannot be modified in different smart contract invocations within the same block of chain.

Hyperledger Fabric guarantees

You probably know the CAP theorem. It states that in the distributed system only two out out of the consistency, availability and partition tolerance guarantees can be achieved at the same time. Even if the Wikipedia is not the best source of information about the CAP theorem (you should definitely read this great article by Martin Kleppmann), it may give you a good intuition of the concepts:

Consistency: Every read receives the most recent write or an error. Availability: Every request receives a (non-error) response — without the guarantee that it contains the most recent write. Partition tolerance: The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes.

In terms of the CAP theorem the blockchain as a distributed system achieves partition tolerance, but the second guarantee depends on the way how the blockchain nodes agree on the state.

Such blockchains like Ethereum and Bitcoin rely on probabilistic consensus algorithms, which guarantee ledger consistency to a high degree of probability. The probability that a transaction is “final” increases as the block with the transaction gets deeper into the chain. For instance, it is recommended to consider Bitcoin transaction final when it is at least 6 blocks deep in the chain. In this case we have probabilistic finality and the blockchains that supports it favor availability over consistency.

The is a second group of blockchains that provides absolute finality. Once a block is added to chain, it is considered final. The blockchains that support this kind of finality favor consistency over availability. Hyperledger Fabric belongs to this group, along with many other private blockchains.

Hyperledger Fabric, before applying the changes made by smart contracts, checks which keys in the state were read and which keys were updated. If, for instance, three smart contracts try to update the same key, there is a conflict and two of them fail with no state change.

Have a look at this quite a long excerpt from the documentation:

Hyperledger Fabric has concurrency control whereby transactions execute in parallel (by endorsers) to increase throughput, and upon commit (by all peers) each transaction is verified to ensure that no other transaction has modified data it has read. In other words, it ensures that the data that was read during chaincode execution has not changed since execution (endorsement) time, and therefore the execution results are still valid and can be committed to the ledger state database. If the data that was read has been changed by another transaction, then the transaction in the block is marked as invalid and is not applied to the ledger state database. The client application is alerted, and can handle the error or retry as appropriate.

And this is the way Hyperledger Fabric provides consistency in terms of the CAP theorem. Or linearizability if you want to use a better word (see this post again). And this is the reason why each block appended to the chain is considered final.

What does it mean for our IncrementerContract ? It means that if the getValue or increment smart contract succeeds, we have access to the most recent state. If there is a possibility that this is not the most recent state, the smart contract fails.

How to increment in parallel

There is a great official repo on GitHub: fabric-samples . You can find here, among others, some practical guidelines how to write your chaincodes to achieve high throughput (I skipped some technical details in the quote below):

This network is used to understand how to properly design the chaincode data model when handling thousands of transactions per second which all update the same asset in the ledger. A naive implementation would use a single key to represent the data for the asset, and the chaincode would then attempt to update this key every time a transaction involving it comes in. However, when many transactions all come in at once, (…) another transaction may have already updated the same value. Thus, (…) a large number of parallel transactions will fail.

In other words: we have the consistency at the cost of lower availability in case of concurrent smart contracts.

Another great resource about handling this kind of situations is an article How to prevent key collisions in Hyperledger Fabric chaincode by Ivan Vankov (known as gatakka). The author proposes four approaches:

No duplicated keys: You can avoid using the same keys, what is easy to implement and achieves high throughput, but you cannot create business logic in the chaincodes, since the state is dispersed over unrelated keys. Put requests in queue: The client side is responsible for managing a queue of smart contract invocations (it takes care of the possible key collisions). This is a non-performant solution and the application layer takes a lot of responsibility that might be outsourced to the chaincodes. Running total: You have no duplicated keys but there is also a separate smart contract that calculates the total state. This solution is performant, however it might be difficult to handle validation on the state. For real applications you will probably end up with multiple operation statuses and handling transitions among them. Batching: Single smart contract handles a list of operations. Even if you execute batches in single thread, the throughput might be still very high. Besides it is quite easy to implement this solution, the application layer might not know the chaincode business logic and the data consistency is preserved.