Building a Corda-based Blockchain Trade Finance Platform for Marco Polo Network
We recently came across a blog post that looked at 34 top real-world blockchain projects. This finding was quite interesting:
“Looking into all 34, I found that 13 are already dead (including one that has been killed by the SEC), 6 are only useful within the crypto & NFT ecosystems and not in the “real world” and 14 use Blockchain in a way where removing the blockchain would not impact functionality at all, or make the product better.”
Our interpretation? That people get excited about using innovative technology, but what matters most is that the technology actually works in real-world scenarios.
These days users push for more and popular applications can see their user base grow exponentially. You need a strong engineering team that knows how to make technology work and scale.
Building a Corda-based Blockchain Trade Finance Platform
Here at Lohika, we know how to design, implement, deploy, and run in production real-world blockchain solutions. Our client Marco Polo Network manages a global trade finance platform. We are implementing an open API-driven platform powered by R3 Corda, a permissioned and highly secure blockchain platform that is ideal for financial markets.
From an engineering perspective, it is a peer-to-peer network of Nodes, each representing a party on the network. These Nodes run Corda applications (CorDapps) and transact between Nodes using public or confidential identities. When one or more Nodes are involved in a transaction, the transaction must be notarized.
Notaries are a specialized type of Node that provide uniqueness consensus by attesting that, for a given transaction, it has not already signed other transactions that consume any of the proposed transaction’s input states.
When implementing your business flows as CorDapps Contracts, States and Flows are the core abstractions to deal with. Corda is implemented in Kotlin and supports Kotlin and Java for CorDapp development.
Here are a few Corda considerations and details related to our project with Marco Polo Network.
In the context of a CorDapp, Contracts define rules for verifying transaction inputs and outputs. The goal of a contract is to ensure that input and output states in transactions are valid and to prevent invalid transactions.
Contracts operate only with data that is present within a transaction and is executed in an internal sandbox environment. Corda ensures Contract validation executes exactly the same code for all the participants: it is the same single jar file distributed across all the participating nodes. If there is any external validation required before signing a transaction (e.g., talking to an external API) – it needs to be implemented as a part of a Flow.
During the implementation of the trade finance platform, the Lohika team was faced with complex flows that involved up to five parties with different roles (e.g., Buyer, Buyer Bank, Seller, Seller Bank and Funder) to name a simple scenario of payment commitment. Technically, each party is executed on a different Node on the network and often has limited visibility into information that it is authorized.
This adds significant complexity when implementing flows. Each of them needs to perform a different set of validations before signing a transaction. We’re in a complex distributed transaction scenario in which any of the stakeholders can respond that the transaction is not valid. This results in a complex situation of how to handle cases when it is not all happy-path scenario.
Our team learned how to leverage the Corda Persistence API which offers developers the option to expose all or some parts of a contract state to an Object Relational Mapping (ORM) tool to be persisted in a Relational Database Management System (RDBMS).
Corda stores state as a sequence of “snapshots.” There is a way to query the latest state, which is referred to as the Unconsumed state. However, it is available in a binary format that is accessible only via the Corda API, which is not easy to work with. On the other hand, the Persistence API allows us to store state in a format that is the best for a use case (e.g., RDBMS or NoSQL). In our case, the team is using Persistence API to save state to Elasticsearch for flexible and quick search scenarios.
Corda is an evolving technology and there are no established best practices, so a lot of solutions are greenfield. This results in many unexpected things when developing CorDapps and running them in production. Let us cover some issues that the Lohika team was dealing with during the implementation.
- Memory leaks
- Unexpected exceptions that crash the entire Corda Node
- When running a Flow, Corda saves Checkpoints that need serialization and deserialization of objects. Sometimes Corda cannot serialize Kotlin objects as is. There was a bug that our team reported to the Corda team and they fixed it — the bug related to map operations for collections. The complexity comes from the asynchronous manner of Checkpoints when you learn it does not work at the end of a coding day. Back to the debugger, then.
- Data consistency issues in a rollback scenario of a Flow with a couple of sub-Flows
All these issues were unexpected, but now the team knows how to handle and avoid them.
No code is useful until it is deployed in production and brings business value to end users. With Corda, every user owns an instance where they deploy their CorDapps. The minimal requirements for an instance are 4 CPUs and 8GB of RAM.
Regardless of the amount of work to do, you keep this instance running, which means that you pay for it day in and day out. If you make a single transaction per day or week, your costs remain the same. After considering these excessive costs, the team decided to tackle it.
We came up with an approach to the categorization of Nodes based on usage scenarios and their load. Now we can run production environments with a minimal instance of 1 CPU and 2GB of RAM for these exceptionally light load scenarios.
Once we get our CorDapps running in production, the world does not stop. So, we had to make changes to them and upgrade them. We made changes to States, Contracts, Services, Flows, Utilities, and library functions.
On the Corda platform, there is no such thing as a “SQL migration.” Corda offers versioning that lets you upgrade in a one-by-one manner. When it comes to a massive upgrade, it could take ages to complete.
Another aspect here is that each party in a network is running its own independent Corda node. So, it is not possible to guarantee that all the nodes will upgrade to the latest version at the same time.
The Lohika team came up with a not-so-obvious solution of running upgrades as a separate flow to address this situation. A dedicated migration flow provides great flexibility to perform migrations of different environment deployments independently from each other, each time migrating data to the latest version, supported by all parties.
There’s much more technical expertise that our team gained with Corda Enterprise Platform over the last four years. The Lohika team contributed to the re-architecture of a v1 Corda-based system for Marco Polo Network.
Our engineers participated in design and architecture decisions, implementation, testing and running it in production. The outcome is that the new v2 platform now runs 35,000 times faster. Feel free to contact us if you want to learn more.
Hear from Shane O’Flynn, Head of Engineering at Marco Polo Network on what it’s like to work with Lohika.