Blockchain Interoperability Solution: How Chainbridge Can Be A Way Out?
Software Engineer - 26 May 2021 -
Software Engineer - 26 May 2021 -
Blockchain technology offers providential results. Its potential for improving business processes, providing transactional transparency and security in the value chain, and reducing operational costs is obvious.
The past few years have seen continuous growth in blockchain-related projects. It signifies that developers are leveraging blockchain’s capabilities by thinking outside the box. Besides, we have to understand there is no perfect solution to address all blockchain needs at once.
Each day the number of solutions that rely on blockchain technology is increasing. But, the technology’s evolution is taking a hit due to the lack of interoperability among blockchain solutions. Many solutions are available for blockchain interoperability, all having their pros and cons. I have used one such solution, Chainbridge, which is an extensible cross-chain communication protocol. It currently supports bridging between EVM and Substrate based chains.
In this blog, we’ll discuss a specific use case of supply chain management that uses the blockchain interoperability between substrate and ethereum chains.
Blockchain holds great promise in the area of supply chain management. It can improve supply chains by enabling faster and more cost-efficient product delivery, enhancing product traceability, improving coordination between partners, and aiding access to financing.
The usecase I have is a simplified version of champagne bottle supply-chain management. I did so to learn and showcase substrate development and the interoperability between Substrate and Ethereum blockchains. By using this application, the end consumer can track and verify the authenticity of the champagne bottle.
Every bottle created will have a unique id, which we will use to track it further down the process. The use-case source code can be found here. Now let’s look at the list of actors and their respective roles in the system.
For simplicity, I will go with these four types of actors. Their roles are as follows.
The entire supply chain process of the application is built with two modules.
Registrar: Registrar pallet is responsible for registering and keeping a record of various actors and bottles in the system. It exposes some functions like registerManufacturer(), registerCarrier() etc to register the members of a particular type. A manufacturer can invoke the registerBottle() function to register a new bottle with a unique id in the system.
Bottle Tracking: The bottle shipment process is tracked using this module. Functions registerShipment() and trackShipment() are used for tracking the bottle from shipment registration to delivery to the retailer. For final customer sell, sellToCustomer() function is called, which transfers the bottle ownership to the end customer.
Chainbridge pallets –
Chainbridge, example-pallet, and example-erc721: Chainbridge provides these three pallets for interchain communication. Follow Chainbridge-substrate for their documentation.
Let’s discuss the entire process from bottle creation to end customer sales, step by step. We’ll have a look at the external function used to interact with the application for each step.
First of all, we have to register various actors in the system. There are four types of actors. So, we have to use four functions, registering each of them using the registrar pallet. These are registerManufacturer(), registerCarrier(), registerRetailer() and registerCustomer(). All 4 functions have the same function signature, one is explained below.
It takes no argument. The caller of the function is registered as a manufacturer. There can be any number of manufacturer, carrier, etc. If there are multiple manufacturers, all can invoke this function separately to register themselves. The same goes for carriers, retailers, and customers.
A manufacturer can register a new bottle.
The manufacture will invoke this method from the registrar pallet providing the BottleId to be registered.
The shipment will be registered by the manufacturer.
registerShipment(id: ShipmentId, carrier: AccountId, retailer: AccountId, bottles: Vec<BottleId>)
To register a shipment, the manufacturer will provide a unique ShipmentId, account Id of the carrier who will carry the package, account id of the retailer to which shipment has to be delivered and the list of bottle ids to be delivered.
The assigned carrier will pick up the shipment from the manufacturer and deliver it to the retailer.
trackShipment(id: ShipmentId, operation: ShipmentOperation)
The carrier will provide the Shipment Id and the operation that it wants to perform on the shipment for tracking a shipment. The ShipmentOperation is an enum that holds two values: Pickup and Deliver. After the delivery operation is completed, the bottle will be in the ownership of the retailer.
In all the steps performed before, it is assumed that all the payments have been made off-chain. To sell the bottle to the end customer, I have assumed that the process will be initiated off-chain where both the parties (Customer and Retailer) will agree on how many bottles have to be sold and the total amount. Once the customer transfers the agreed amount to the retailer’s substrate account, the off-chain system will automatically trigger/invoke a method in the substrate chain, i.e., sellToCustomer(), which will only transfer the ownership of the bottles to the customer.
sellToCustomer(customer: AccountId, bottles: Vec<BottleId>)
This function will be invoked using the retailer’s account, providing the customer’s account id and the bottles to be sold.
The customer can transfer the amount to the retailer’s substrate account by any means. But to support our interchain operability use case perspective, let us assume that the customer has some Ethereum smart contract tokens. This token holds some equivalent value to the substrate native token. Now the customer wants to transfer these Ethereum tokens directly to the retailer’s substrate account. This interchain communication can be achieved using Chainbridge, a cross-chain communication protocol.
Chainbridge is a modular multi-directional blockchain bridge. Currently, it supports interoperability between Ethereum and Substrate-based chains. There are three main roles:
Both sides of the bridge have a set of smart contracts (or pallets in the substrate), where each has a specific function:
Below diagram is a summarized workflow of Chainbridge:
Chainbridge currently relies on trusted relayers. However, it has mechanisms to stop power abuse and mishandling of funds by any single relayer.
Follow the instructions at provenance usecase repo to start the substrate chain.
The command below will start the geth instance.
|docker run -p 8545:8545 chainsafe/chainbridge-geth:20200505131100-5586a65|
To deploy the contracts onto the Ethereum chain, run the following:
|cb-sol-cli deploy –all –relayerThreshold 1|
|cb-sol-cli bridge register-resource –resourceId “0x000000000000000000000000000000c76ebe4a02bbc34786d860b355f5a5ce00” –targetContract “0x21605f71845f372A9ed84253d2D024B7B10999f4”|
# Register the erc20 contract as mintable/burnable
cb-sol-cli bridge set-burn –tokenContract “0x21605f71845f372A9ed84253d2D024B7B10999f4”
# Register the associated handler as a minter
cb-sol-cli erc20 add-minter –minter “0x3167776db165D8eA0f51790CA2bbf44Db5105ADF”
Select the Sudo tab in the PolkadotJS UI. Choose the addRelayer method of chainBridge, and select Alice as the relayer.
Select the Sudo tab and call chainBridge.setResource with the below method parameters:
Method: 0x4578616d706c652e7472616e73666572 (utf-8 encoding of “Example.transfer”)
Using the Sudo tab, call chainBridge.whitelistChain, specifying 0 for ethereum chain ID.
Here is an example configuration for a single relayer (“Alice”) using the contracts we’ve deployed. Save this JSON inside a file and name it config.json.
“useExtendedCall” : “true”
First, pull the Chainbridge docker image.
|docker pull chainsafe/chainbridge:latest|
Then start the relayer as a docker container.
|docker run -v $(pwd)/config.json:/config.json –network host chainsafe/chainbridge –testkey alice –latest|
With this setup complete, now we should be able to do fungible transfers over the two chains.
In the Polkadot JS UI select the Developer -> Extrinsics tab and call example.transferNative with these parameters:
To query the recipients balance on ethereum use this:
|cb-sol-cli erc20 balance –address “0xff93B45308FD417dF303D6515aB04D9e89a750Ca”|
If necessary, tokens can be minted:
|cb-sol-cli erc20 mint –amount 1000|
Before initiating the transfer we have to approve the bridge to take ownership of the tokens:
|cb-sol-cli erc20 approve –amount 1000 –recipient “0x3167776db165D8eA0f51790CA2bbf44Db5105ADF”|
To initiate a transfer on the ethereum chain use this command (Note: there will be a 10 block delay before the relayer will process the transfer):
|cb-sol-cli erc20 deposit –amount 1 –dest 1 –recipient “0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d” –resourceId “0x000000000000000000000000000000c76ebe4a02bbc34786d860b355f5a5ce00”|
Blockchain-based networks are being built to offer specific capabilities. These different networks should be able to share their data and talk to each other to make the most out of these capabilities, which makes blockchain interoperability a must.
Chainbridge offers an excellent solution with its modular and multi-directional design. They have been continuously working towards enhancing the bridge system and their community channels are very active and supportive. With version 1.0, Chainbridge has built the scaffoldings necessary for message passing functionality. The upcoming versions of Chainbridge can achieve this functionality in a decentralized and trustless manner to ensure there are no central points of failure.