This is the final blog of the ticket booking system I’ve designed. I want to review the whole process: design business logic, build CorDapp, build RPC, build front-end web pages, and code add-ons. This blog will mostly focus on the most time-consuming issues I met in the development process and how I solve these issues. It is not tech-oriented, but more like a method of finding resources to solve problems.
Overall, there are three main issues covering coding and configuration parts: write flow conversations, front-end display, and configuration issues. Most of them have been talked about in previous sessions, so it’s more like a review of the whole developing process 😃
Writing flow conversations
The most important step is to know how to debug and read error messages: here’s the file I usually go to check errors.

Let me then review how to initiate flows:
Firstly, we need to indicate the flow that can generate conversations with other flows by the annotation @InitiatingFlow. Then we use function initiateFlow(partyName)
to start a communication with its counterparties. After that, we can use subflow()
function to make a transaction. There are some defined flows in the library to apply directly, some of which return a completed transaction, and some just work as a wrapper to make things easier. IssueTokens()/ CreateEvolvableTokens()
belong to the first type, because they also contain a responder flow signing the whole contract. For the second flow, the most common flow is the SendStateAndRefFlow()
, which is used to send a list of input StateAndRef to its counterparties that wish to verify the input’s integrity by resolving and checking the dependencies as well. So in its responder flow, it invokes the ReceiveStateAndRefFlow()
at the right point in the conversation to receive the input state and ref and perform the resolution back-and-forth required to check the dependencies. Apart from above mentioned two types, we can also use the session.send()
to send other self-defined information., the other parties receive that piece of information using receive()
function.
For building the content of the transaction, we can use TransactionBuilder(notary name)
to specify a notary. For that contract, we use addOutputState()
, addCommand()
, addInputState()
to add specified constraints.
To sign the transaction, we can just use serviceHub.signInitialTransaction()
to get a partially SignedTransaction with that node’s signature attached. After that, we get a fully SignedTransaction with all the counterparties’ names on it by CollectSignaturesFlow()
function.
Show ‘expire’ image on the expired tickets
This is mostly a javascript thing. To achieve this, we first need to get the date information of the specific ticket by the function $http. get(url).get()
. Then we need to tackle that response, actually what we get here is a whole booked ticket list. So we need to use angular.forEach to search the loop.
The current date’s format is gotten by the getFullYear()
, getMonth()
and such functions, then we use the ‘isExpired’ variable to control the display of the image. The HTML file uses ng-show
to control the expire sign image.
Configuration issues
As for the configuration part, I think there are several points that need attention:
- Configure java:
Make sure the version of JavaSDK is version 8 and the IntelliJ IDEA versions should be 2017.x, 2018.x, 2019.x, or 2020.x; and Kotlin plugin version 1.2.71.
All the configuration processes can follow the video.
- Configure RPC port:
For this task, we only need to be more careful and make sure we have modified all the necessary files. Here I will list all the file names that need to change, though I have already listed them in the Build RPC section.
project’s build.gradle
:
rpcUsers = [[ user: “user1”, “password”: “test”, “permissions”: [“ALL”]]]
Configure RPC user for each Corda node:
node {
name "0=Agency,L=London,C=GB"
p2pPort 10008
rpcSettings {
address("localhost:10009")
adminAddress("localhost:10049")
}
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
}
application.yaml
Specify the host address of the Agency:
Agency:
host: localhost:10009
AppConfig.kt
@Configuration
open class AppConfig : WebMvcConfigurer{
@Value(value "/$Agency.host")
lateinit var AgencyProxy: String
@Bean(destroyMethod = "")
open fun AgencyProxy(): CordaRPCOps {
val agencyClient = CordaRPCClient(NetworkHostAndPort.parse(AgencyProxy))
return agnecyClient.start(username: "user1", password: "test").proxy
}
}
Those are the issues I think are the hardest to solve or the easiest to forget in the development process. If you are trapped in the same dilemma, hope this blog can help you out.
