How to code your CorDapp in Kotlin

Corda Oct 25 2022 By: Lin Chen




How to code your CorDapp in Kotlin
Lin Chen
Lin Chen Developer Evangelist Intern
Share this post:

Tokens represent any kind of asset on your network. Token SDK provides the easiest way for you to create them.

This blog is mainly about my coding experience related to my designed ConcertTicketBooking system. As a recap, my design of the app is shown below. If you are not familiar with the logic design, you can refer to this blog for more details.

Logic design of the ConcertTicketBookingn system
Logic design of the ConcertTicketBookingn system

Correspondingly, I’ve divided the article into three main sections — writing the state, writing the contract, and writing the workflow.

Writing the state:

Our ticket booking system already has four defined states — Ticket State, VenueOwnership State, Money State, and Request State. In order to code those states, we should define their types and their contained parameters.

An item’s type usually tells us about its basic properties. In Corda, we can have OwnableState, LinearState, QueryState, and so on.

We would like to have our asset presented in a Token format using the TokenSDK. In the TicketBookingSystem example, Ticket, VenueOwnership, and Money have actual value in the transaction and can be seen as assets, hence we are defining these states as Tokentypes. The Request doesn’t stand for any asset, but each request is unique so it can be seen as a LinearState instance.

For the TokenTypes, whether it is fungible or not depends on the specific use case. In this example, my design is each person who wants to see the concert must book the ticket by himself/herself and can only book once. This means the Ticket can only have one owner and can’t be shared. It works the same for the VenueOwnership, which can only be lensed to one agency at a time. Since the Ticket state and the VenueOwnership state are already the smallest unit of the asset, hence we make them Non-Fungible Tokens. But for the Money, I just assume it’s from the same issuer and its smallest unit is of the same value, hence the Money can be defined as a Fungible Token.

To define the contained parameters:

As for the VenueOwnershipState, the concertID differentiates each lease; the hallID specifies which concert hall is being leased; the issuer is usually the venue holder; the contractor specifies who will this concert hall be rented to; the description is about what event is going to be held here; the startTime and endTime defines when does lease start and end; the maxSeat tells what the maximum number of people is expected to come; the soldOut records the number of seats that have been sold; the price of each seat is also defined in this class.

concertID: String
hallID: String
startTime: String
endTime: String
issuer: Party
contractor: Party
description: String
maxSeat: Int
soldOut: Int
price: Amount<Currency>
Class diagram for the VenueOwnershipState

For the Ticket state, it defines the parameters for the ticket details. So there’s no doubt that each ticket has a startTime, an endTime with the hallID. That information reminds the customer of the exact place and time of the concert. The ticket also needs to contain the name of the issuer, the agency, and the buyer. Therefore, when the venue staffs check the ticket, they will know that this ticket is issued by them and can confirm its validity.

startTime: String
endTime: String
concertID: String
issuer: Party
agency: Party
buyer: Party
Class diagram for the TicketState

For the Request state, it has the concertID that points to the specific performance, the name of the buyer, and the receiver who is in charge of tackling the customer’s ticket request (usually the Agency).

requester: Party
receiver: Party
concertID: String
Class diagram for the RequestState

For the Money state, we use the FiatCurrency class that is pre-defined in R3’s library.

With the following class diagram, we will grab a clearer overview of what’s the relations among states.

Class hierarchy of Ticket Booking System
The class hierarchy of Ticket Booking System

Writing the contract:

The rules in the contract help check whether each state meets the business logic requirement.

Each defined state belongs to a contract and needs to be annotated using @BelongsToContract annotation. Within the contract, the details of each state are clearly listed during the transaction.

For the contract of the VenueOwnership. When created, there must have a VenueOwnershipState as its output. In that state, the start time should be later than the current time, the end time must be later than the start time. The soldOut number is less than the maxSeat. The issuer is not the same as the contractor.

For the contract of the TicketState, the limitation is the end time is later than the start time when creating the ticket.

Writing the workflow:

Corda’s @InitiatingFlow annotation enables a node to initiate a communication with a counterparty and request its counterparty to start its side of the flow communication.

we call the starting flow the InitiatedFlow. Its counterparty generates the ResponderFlow. The @InitiatedBy annotation differentiates the ResponderFlow from the InitiatedFlow. Those two flows are essential for a complete SignedTransaction.

After knowing the definition of the InitiatedFlow and the ResponderFlow, we are going to complete the workflows step by step:

  1. IssueCurrency flow: The Bank issues money, we can refer to the dollartohouse instance to build this flow.
  • Generally, this implementation is about creating the FungibleToken instance and then issuing it.
  • This IssueCurrency flow initiated by the Bank node takes three inputs: the currency’s type (whether it’s GBP or USB), the amount value, and the receiver. Those three inputs help define the properties of the FungibleToken for the fiat currency to be issued.
  • We can utilize the IssueTokens() flow in Corda’s library to complete the issuing process. Since the IssueTokens() flow returns a complete transaction(SignedTransaction), there’s no need for us to write a ResponderFlow by ourselves.
IssueCurrency flow

2. VenueSale flow: The VenueHolder sells the venue’s ownership to the Agency and charges it. The selling process is all about creating, issuing, and transferring the VenueOwnership.

  • In the implementation, the VenueHolder node initiates the VenueSale flow, which takes each concert’s basic information as inputs and wraps it into a new VenueOwnershipState.
  • In this InitiatedFlow, we use CreateEvolvableTokens() flow to add VenueOwnershipState (EvolvableTokenType) onto the ledger.
  • Then we clarify the ownership is a NonFungibleToken and issue it. But the problem is the NonFungibleToken’s constructor only accepts a TokenType rather than a LinearState. To solve this, we use toPointer() function to convert the VenueOwnership from a ContractState type to the TokenType. We still use IssueTokens() function to issue this token piece.
  • Before transferring the ownership, the VenueHolder is expected to charge the rental fee from the Agency (counterparty). To achieve this, we use the initiateFlow() and sendSession() function to inform the Agency of the 7836 5158payment amount. And the InitiatedFlow will keep waiting if it doesn’t get any response from the ResponderFlow. One thing to note here: In this use case, the rent fee is calculated by the maxSeat*fixed selling price per seat/2.
  • The ResponderFlow with the @InitiatedBy annotation is invoked automatically by the InitiateFlow() function in the InitiatedFlow. In this ResponderFlow, the Agency sends the ReferencedState of its FiatCurrency back to the VenueHolder by calling SendStateAndRefFlow(). This subflow not only activates the blocked InitiatedFlow by calling the ReceiveStateAndRefFlow<FungibleToken>()but also provides the VenueHolder with the right to move those FiatCurrency from the Agency’s account to its own account using addMoveNonFungibleTokens() function.
  • After that, the contract with movable FiatCurrency (Agency -> VenueHolder using addMoveTokens() function) is added with VenueOwnershipState (VenueHolder -> Agency using addMoveNonFungibleTokens() function) are sent by the VenueHolder through the InitiatedFlow. The ResponderFlow will automatically sign after passing the checkTransaction() function. Both sides reach a deal!
InitiatedFlow for the VenueSale(start by the VenueHolder)
InitiatedFlow for the VenueSale(start by the VenueHolder)
VenueSale’s ResponderFlow. The responder is usually the Agency.
VenueSale’s ResponderFlow. The responder is usually the Agency.

3. RequestTicket flow: The Buyer then sends the ticket request and money to the Agency. This request specifies the concertID the Buyer wants to book. Then the Agency checks the availability of seats for that concert, makes the decision, and updates this transaction to the VenueHolder.

  • Firstly, the Buyer node initiates the RequestTicket flow. We pair the currency type with the amount and wrap the concertID in the RequestState.
  • Then the Money and this RequestState are respectively sent to the Agency using SendStateAndRefFlow flow and the session.send() function.
  • In the ResponderFlow, the Agency firstly checks the seat’s availability for the specific concert using filter() function. If there’re seats left, it will accept the payment using the addMoveTokens() function. Then it appends the RequestState to the contract and sends it back to the Buyer as a receipt for the payment.
  • The contract gotten from the Agency is signed automatically by the Buyer using an inner class SignTx that contains the checkTransaction() function.
  • Meanwhile, the transaction ID of this receipt is recorded by the Agency so that this transaction can be updated to the VenueHolder using the ReportManuallyFlow (referenced the observableStates sample).
RequestTicket Flow generated by the Buyer
RequestTicket Flow generated by the Buyer
RequestTicket’s ResponderFlow
RequestTicket’s ResponderFlow

4. IssueTicket flow: Finally, the VenueHolder checks the reported the RequestState in its vault and uses the RequestID to issue the ticket to the Buyer. Also, it updates the VenueOwnership’s seat information.

  • The requester’s name and the concertID of the new TicketState are gotten by querying the RequestState’s RequestID. Moreover, the new TicketState’s startTime and endTime are gotten by querying the VenueOwnershipState’s using the concertID.
  • Then like what we’ve done to the VenueSale flow, we issue this new TicketState as Non-FungibleTokens. One thing that is different from the VenueSale flow is that in this transaction, there’s no need for the VenueHolder to charge the Buyer, so it can issue the ticket to the Buyer directly using IssueTokens without addMoveNonFungibleTokens() function.
  • The final step is to update the information of the VenueOwnershipState.
IssueTicket flow started by the VenueHolder
IssueTicket flow started by the VenueHolder
UpdateVenueOwnershipState flow and its responder flow
UpdateVenueOwnershipState flow and its responder flow

The above-mentioned sections are all about my journey of turning a design into a real application. From my perspective, writing the flow is the most difficult part of the whole process, but the open-sourced sample code really helps me a lot when I got stuck. I hope my explanation of codes and use of functions can somewhat contribute to your own work!

Lin Chen
Lin Chen Lin Chen is a Developer Evangelist Intern at R3, an enterprise blockchain software firm working with a global ecosystem of more than 350 participants across multiple industries from both the private and public sectors to develop on Corda, its open-source blockchain platform, and Corda Enterprise, a commercial version of Corda for enterprise usage.

Leave a Reply

Subscribe to our newsletter to stay up to date on the latest developer news, tools, and articles.