Creating your first Quorum Blockchain dApp in Java Spring Boot
The first time I tried to pick up the ropes on Quorum Blockchain, I was literally all over the place. I recall shovelling through tons of documentation and trying out umpteen code snippets before I could put it all together into a working dApp. It was all there in bits and pieces, but a single resource that could help me build my starter application end-to-end was amiss. Hopefully, this article can take away some part of that hassle, or at least narrow it down.
Indeed, Blockchain is quite different from your everyday technologies and probably not one that can be spun up into an app just by browsing a few examples. But, once you understand the underlying architecture and flows, I doubt you will be able to pull yourself away from coding your very first Blockchain based distributed application.
Heads up, a popular use case of Blockchain is the transfer of “value” securely over existing internet pipes, like cryptocurrency. However, Ethereum Blockchain supports other operations via Smart Contracts as well as a global distributed ledger. It also has support for public and private transactions. This makes it a perfect candidate for use cases involving multiple parties in an eco system, who would like to transact privately with each other, while sharing common data. For eg. a consortium of banks or commodity traders. The application used herein addresses a similar use case. Probably not the right fit if you are looking for a cryptocurrency-kind-of application.
So, what’s this dApp all about? I have used Quorum, an Ethereum based Blockchain implementation to create a very basic messaging application in Java Spring Boot. Since it’s not about transferring “value” between participants, aka Ether in the Ethereum world, I have used events in Solidity smart contracts to allow participants to start threads and subsequently send messages. Participants receive messages by listening to logs of these events. Spring Boot connects to the Quorum Blockchain via Web3j. Privacy of events or transactions is managed by Quorum’s Transaction Manager — Tessera. In Ethereum, transaction processing is driven by Gas, a unit to measure processing power or resources.
Before we get down to coding, let’s go back to the previous paragraph. Do the highlighted words look conceptually familiar?
If the answer is no, it’d be a good idea to lay some ground work before proceeding. At the bottom of this page, are a couple of references that will clarify all the concepts you need to get started, step by step. You’d be ready to come back in an hour or two.
If the answer is yes, fire up that IDE and terminal. Let’s get cracking!
This is how the dApp looks like:

Each Quorum node is a participant that can send and receive messages to and from other nodes. Each node will have an application instance of its own. An instance connects to its node using Web3j, via the URL of the node and an account address registered with the node. Each instance needs to run on a separate port and all instances together make up the distributed application. The instances send and receive messages via the Quorum. The code for node1 (Application Instance 1) can be found here. In Step 10, I have explained how to deploy application instances for the other participant nodes.
Step 1 : Setting up a Quorum network
The 7nodes repository is the easiest way to get started with Quorum. There are step by step instructions on how to set it up and shoot around some basic commands on contracts and transactions. I used the Docker setup. If you have never used a Quorum network before, it’d be good to follow through the entire readme.
Step 2 : Connecting to Quorum from Java
I have used a very basic Spring Boot Maven application with Spring Web and Thymeleaf as dependancies. To connect to Quorum, include two Web3j libraries as maven dependancies — core and quorum.

Refer to the complete pom.xml here. You’ll notice a plugin called web3j-maven-plugin. I will explain why it’s needed in the next step. For now, let’s see how we connect to Quorum using the Web3j library.
Let’s look at QuorumConnection.java. All Quorum related parameters are defined here. In order to connect with a Quorum node, we create Quorum and Admin objects by passing the URL of the node. Run docker ps in the terminal and note the port of the first node, quorum-examples_node1_1. This is the URL used in the application — http://localhost:22000


Other parameters defined are used when we create a Client Transaction Manager in later steps.
nodeKey : Every node in Quorum is protected by public — private key pair generated by the privacy transaction manager. In the keys folder of the 7nodes repository, you can see a bunch of public private key pairs (Eg. tm1.key and tm1.pub) These have been assigned to nodes in the Tessera initialisation script. Once assigned, the public key can be used to send and receive transactions. A node is identified by its public key. tm1.key and tm1.pub get assigned to node1. We assign the value of tm1.pub to nodeKey in QuorumConnection.java in the application.



nodeAddress : We have talked about sending and receiving transactions quite a few times now. Who sends these transactions? — Accounts do. Accounts are nothing but addresses registered with a Quorum node, and can be protected with a private key and passphrase. Head over to your terminal (ensure 7nodes are up and healthy by executing docker ps) and execute docker exec -it quorum-examples_node1_1 geth attach /qdata/dd/geth.ipc Once you have attached to the console, execute the web3 command web3.eth.accounts You will get an array of accounts registered with this node.

But hold on, when did we assign this account to node1? Refer to the raft-init.sh script here. It picks up keys (or account specifications) from the keys folder (key1, key2 etc) and adds them to the /qdata/dd/keystore folder of the corresponding node.

Let’s take the example of keys/key1. raft-init.sh stores this in the keystore folder of node1, registering it as an account of node1. You will notice, the address field in key1 is the same as the one when we ran the web3.eth.accounts command above, with a “0x” prefixed. Let’s assign it to nodeAddress in QuorumConnection.java in our application

Step 3 : Writing a Solidity contract
Next, we create a simple contract Thread.sol

Access the code here.The contract has three members — participants, contract address and messages. It has two methods which emit one event each:
- startThread : This method is invoked after a new thread is created by deploying a fresh contract. It emits an event sendContractAddress to send the contract address of the contract and participant list to all nodes in the thread.
- sendMessageToThread : This message is invoked by any participant who wants to send a message to a thread. It emits an event sendMessage to send the message, sender and participant list to all nodes in the thread.
The participant list is used to identify threads uniquely in the application eg (node1,node7). Objects of ThreadModel.java are used to store threads locally in the application.

When a sendContractAddress event is received, a new ThreadModel object is created. When a sendMessage event is received, an existing object identified by the participant list, is updated with the message and sender. The participant list is always kept sorted to ensure it can uniquely identify a thread.
Step 4: Generating Java wrapper classes for Solidity contracts
Web3j provides a maven plugin to generate Java classes from Solidity contracts. These wrapper classes help to create and interact with the actual contracts in the Quorum network. Let’s go back to plugins section in pom.xml

Here we define configurations for the plugin like source and destination directories. The generated Java classes will be stored in the destination directory. I have already generated the wrapper class, Thread.java, for the contract used in the application.
For using any further contracts, you will first need to install Solidity compiler. You can find instructions here (I used Brew). Once the compiler is installed, the Solidity contracts need to be created in the source directory of the plugin configuration. Next, run a mvn install in the same directory as pom.xml and your wrapper classes should get generated in the destination directory.
Step 5: Creating Client Transaction Manager objects
Whenever the application needs to perform an operation on Quorum, it needs to pass an object of the ClientTransactionManager class.

The following parameters need to be passed:
- Quorum object explained in Step 2
- Address of an account of the node initiating the transaction (nodeAddress explained in Step 2)
- privateFrom — the public key of the node initiating the transaction (nodeKey explained in Step 2)
- privateFor — a list of public keys of other nodes, that are meant to be parties in the transaction
- Sleep duration
- Number of connection attempts
You can see these objects being created in the controller, wherever an operation is being done on the Quorum.
Step 6: Starting a new Thread (Deploying a fresh contract and invoking sendContractAddress event of the contract)
Let’s stay on the controller for now. Refer to the method createNewThread. This is where the application lands when a new thread is created from the UI. Since a new thread is being created, a fresh contract needs to be deployed private to the participants in the thread.

- Create the ClientTransactionManager object explained in Step 5
- Deploy a fresh contract using the deploy method of the wrapper class generated in Step 4. This returns an instance of the wrapper class. Notice the last two BigInteger parameters passed in the method. These are related to Gas required for the execution of transaction on the Quorum. Don’t worry too much about them for now.
- Read the contract address of the freshly deployed contract by invoking the getContractAddress method of the wrapper class instance returned in previous step.
- Call the startThread method of the contract, using the wrapper class instance, passing the contract address and participants list, so that the event sendContractAddress can be emitted to other thread participants, as explained in Step 3. This returns a TransactionReceipt.
Let’s head to the next step to understand how the first message is sent to the thread.
Step 7: Sending a message to a existing Thread (Invoking sendMessage event on the contract)
The first message is sent to the thread right after the sendContractAddress method is called.

The same wrapper class instance is used to call the sendMessageToThread method of the contract, passing the sender node and message parameters. This eventually leads to the sendMessage event being emitted from the contract as explained in Step 3.
When an existing message thread is updated from the UI, we land up in the sendMessageToExistingThread method of the controller. We follow similar steps as in Step 6 to send the message to the thread
- Create an instance of the ClientTransactionManager
- Load the contract for this thread by calling the load method of wrapper class, passing the contract address along with other parameters (How do we get the contract address? — Explained in Step 8) This returns an instance of the wrapper class.
- Using this wrapper class instance, call the sendMessageToThread method, passing the message and sender as parameters. Again, this leads to the sendMessage event being emitted from the contract.
Step 8: Listening to Quorum logs for events:
The threads have been started and messages have been sent, how do the participating nodes come to know of them? By subscribing to logs of Quorum events. Web3j provides an API that uses RxJava Flowables to listen to Quorum events asynchronously. However, the application does not need to listen to all events, just the sendContractAddress and sendMessage ones. To achieve this selective listening, Web3j provides Topic Filters. A topic is nothing but the keccak 256 hash of the event signature. Let’s take the example of sendContractAddress event in our Solidity contract.

The signature of the event is sendContractAddress(string,address) Head over to this online tool to convert this signature into a keccak 256 hash.

This hash, prefixed by “Ox”, is the topic by which we can filter out specifically sendContractAddress event in our listener. Similarly we can create another listener and apply the sendMessage topic filter to listen to specifically sendMessage events. Note that the node will only be able to read logs of contracts, that, it was a private participant in. So, if node2 created a thread between itself and node7, node1 will not be able to read the logs of that contract. Let’s go back to our controller to understand how all of this comes together. Refer to the showThreads method.

- Create a EthFilter object using the following parameters:
1.a Starting and ending block numbers — I have used earliest and latest to listen to all events
1.b Admin object described in Step 2
1.c List of contract addresses whose events you need to listen to. I have left it empty to get events from all contracts
1.d In the appended addSingleFilter method, pass the sendContractAddress topic derived above
2. Next, use the Admin object to create a EthLogFlowable object, passing in the EthFilter object created just above. Call the subscribe method on this Flowable object to start reading logs. For each log:
2.a Extract out the event values into an EventValues object using Web3j’s static method, staticExtractEventParameters and passing in the event from the wrapper class along with the log
2.b Create an event response object specific to the sendContractAddress event provided by the wrapper class and read individual values that the event emitted from the EventResponse object created above.
2.c Since the sendContractAddress event is emitted when a new thread is created, receiving an event of this type means creating a new ThreadModel instance, passing in the participants and contract address received from the event.

Since this instance of the application connects to node1, after the above function, it has all the threads that node1 is a participant in, in the form of a map of participants to ThreadModel instances ( allThreads ). This is passed as threadModels on to the html for display and also for the contract address and participants to be passed as form variables while updating threads. Remember, in Step 7, we pass the contract address to load contract of an existing thread when we want to send a message to it? This is how we get the contract address, as a form variable.

Step 9: Running the application for node1
Now that we have an overview of how the controls flow through the application, let’s give it a spin. First, a quick docker ps in the terminal to ensure all Quorum nodes are up and healthy. Now, build and run the application. Head over to the browser and hit http://localhost:8080/threads Create threads and update them to ensure the application is running the way it should.
Step 10: Creating application instances for other nodes — dApp magic!
For a real distributed application experience , we need to see how it runs across nodes so let’s run this application for another node, say node7. Download the repo into another folder and make the following changes. Refer Step 2 if you need a brush up on any parameter.
- src/main/java/com/sample/quorum/connection/QuorumConnection.java:
1.a node = node7;
1.b nodeAddress = “0xcc71c7546429a13796cf1bf9228bff213e7ae9cc”; ( The address of node7’s account. Refer keys/key7 )
1.c nodeKey = “ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc=”; (The public key of node7’s key pair. Refer keys/tm7.pub )
1.d While creating Admin and Quorum objects, the URL of node7 needs to be passed. On doing a docker ps, we can see quorum-examples_node7_1 running on http://localhost:22006 Pass this parameter while creating Admin and Quorum objects.

2. Since the application instance for node1 is running on localhost:8080, we need to specify a different port for the node7 instance. Head over to src/main/resources/application.properties and add server.port=8081
Build and run the application instance and access it at http://localhost:8081/threads. Try creating a thread between node1 and node7 and update it from both instances to see the magic! (You will need to refresh the /threads page on every new/update thread) If you create a third application instance for another node and create threads between any two nodes, you can see how the third node cannot see that thread. Or, if a thread is created between three nodes, how they can all see it. Play around!


Taking it further:
You may ask, why do we need to save the threads locally in the application, when we have a perfectly fine and secure Blockchain to do that? If you did, good question! Ideally, we just need to map the participants list to the contract addresses in our application. The messages can be sent to/retrieved from the contract based on the contract address. We would need to use a mapping data structure in the contract. The sendMessage event would also not be required as the messages can be fetched from the thread contract on demand. For the sake of simplicity, I have avoided this. However, that would make for a great next step. Go ahead and try to eliminate the need for storing threads locally.
Step 0 : Blockchain 101
If you are looking at this step, the terms in the article didn’t sound too familiar. No worries! Just spend some time on the following links, and you will be coding in an hour. Trust me, you will enjoy it much more when you know how it all works. Do not worry too much about deeper concepts like mining and consensus at this point of time.
1. Blockchain Fundamentals : What is the fuss all about?https://blockgeeks.com/guides/what-is-blockchain-technology/
2. Ethereum : What is Ethereum Blockchain and how does it work? Smart Contracts?
https://www.ethereum.org/learn/#ethereum-basics
Don’t miss : https://medium.com/@preethikasireddy/how-does-ethereum-work-anyway-22d1df506369
3. go-ethereum : The official Go implementation of Ethereum
https://github.com/ethereum/go-ethereum
4. Quorum : How does Quorum build over Ethereum? All answers here
https://github.com/jpmorganchase/quorum
The above reads should make things clear enough to start. To build more complex Web3j-Quorum applications, it would help to gain depth from the following resources:
1. web3.js
https://web3js.readthedocs.io/en/v1.2.0/index.html
2. Solidity
https://solidity.readthedocs.io/en/develop/index.html
3. Web3j
https://docs.web3j.io/
https://github.com/web3j
4. Quorum
https://docs.goquorum.com/en/latest/
