Exploring How Private Transaction Works in Quorum

The first time I heard about Quorum is in the launch video of Enterprise Ethereum Alliance (EEA) back in last February. In that lengthy webcast I saw the demonstration of Quorum and how private transaction is done on top of Quorum (and therefore Ethereum) network.

As EEA currently announced the Architecture Stack, also it is reported that JPMorgan will spin out Quorum. It’s good time to revisit how things go lately.

With knowledge in using geth and how Ethereum works, I begin to explore Quorum, in particular how the private transaction works. Here I am exploring their 7nodes example in their repository.

Quorum: Quick Introduction

Quorum is the open source project developed by JPMorgan (link). The objective is to make Ethereum enterprise ready. Today Ethereum is well known with its public and permissionless blockchain, a smart contract platform, its native cryptocurrency and tokens issued on top of it. To make Ethereum enterprise use, certain features need modification and some additional features are required.

Among these features, my focus is on the private transaction: only those specified target, that is, node(s), will see and process the transaction, while that transaction means nothing to other nodes. The target specification is done through the public key of the node(s).

Here is the portion of wiki about the transaction process and how privacy is achieved.

Set Up a VirtualBox VM with Vagrant for 7nodes

Make sure Vagrant and VirtualBox are installed. Here we clone the quorum-examples.

Image for post
Image for post

Here is the Vagrantfile,

Image for post
Image for post

We see from the Vagrantfile that,

  • A VM of 2 MB is created on Virtualbox
  • Seven ports (22000–22006) are configured as host port / guest port. They are for the seven nodes being simulated.
  • vagrant/bootstrap.sh is executed after the VM is instantiated.

Here is what’s inside the shell script vagrant/bootstrap.sh,

Image for post
Image for post

*** Updated: the original issue of directory change is due to a new vagrant box. And there it is removed. The following works as usual.

To instantiate the VM, use this command,

$ vagrant up

It takes some time to complete the download and setup. Here is the screenshot of finishing.

Image for post
Image for post

We are now ready to ssh into this VM.

$ vagrant ssh
Image for post
Image for post

Setup of 7nodes Example

Overview of the 7nodes Example

The architecture of Quorum is quite simple. It is composed of a Quorum Node and Constellation (which is composed of Transaction Manager and Enclave). See here for more information about the architecture.

Image for post
Image for post
Source: https://github.com/jpmorganchase/quorum/wiki/Quorum-Overview

Note that Quorum Node is built on top of Go Ethereum (geth) client. We use geth console largely in this article.

In this 7nodes example, each node has its Quorum Node and Constellation. The setup is kept inside qdata/ddn and qdata/cn, where n is 1 to 7. Communication between Quorum Node follows normal ethereum p2p way, with certain modification for permission and private transaction handling. Constellation are communicating one another using https (therefore proper server key is required).

As Quorum is not for public and permissionless use, consensus is not achieved through mining process. Quorum here uses Raft as the consensus protocol. The setup therefore includes all the required Raft configuration.

The wiki contains great introduction on the transaction processing, and how Quorum and Constellation interact on private transaction.

The setup requires both raft-init.sh and raft-start.sh to establish the 7 nodes.

Setup Shell Scripts

Image for post
Image for post

We can see in this shell script,

  • Proper files are copied to corresponding directory qdata/ddn.
  • The chain is initialized (with geth) with the same genesis.json. This makes the seven nodes forming a private ethereum network (that is, share the same ethereum blockchain).

Here is raft-start.sh.

Image for post
Image for post

We can see in this shell script,

  • Run constellation-start.sh script to setup constellation
  • Run geth client with proper parameters

Finally we take a look on constellation.sh shell script.

Image for post
Image for post

We see in this shell script,

  • Create directory qdata/cn (n from 1 to 7)
  • Copy appropriate transaction manager keys to the directories
  • Execute constellation-node with parameters

Therefore, after we run raft-init.sh and raft-start.sh, the 7nodes example is setup properly.

Image for post
Image for post
Image for post
Image for post

We can check the geth and constellation processes for 7 nodes are running.

Image for post
Image for post

Here is how it looks after deploying the 7nodes example. We only show three nodes (Node 1, 4 and 7) here as we will build the illustration on these three nodes later in this article.

Image for post
Image for post

Understand the Test Script

Here is the Smart Contract (simplestorage.sol).

Image for post
Image for post

When the contract is deployed, a value (initVal) is required. This value is stored in the variable storedData. This contract comes with two functions: get(), which returns the storedData, and set(), which modifies the storedData with the supplied value.

Quorum Example provides a JavaScript file to execute the test. Let’s first take a look on script1.js.

Image for post
Image for post

On Line 11,

var simple = simpleContract.new(42, {from:web3.eth.accounts[0], data: bytecode, gas: 0x47b760, privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]}, function(e, contract) { … }

Here we see,

  • Variable simple is a new deployed object of simplestorage contract
  • The initial value is set to 42
  • The contract is deployed by eth.accounts[0]
  • This is a private transaction, as it contains privateFor value

We double check and can see the address “ROAZBW…” is the public key of Transaction Manager of Node 7.

Image for post
Image for post

Therefore, this transaction is a private transaction, and only Node 7 can receive and understand it.

According to the design, this transaction is still visible in all nodes. But with privateFor specifying Node 7 alone, only Node 7 can see the value while others see nothing. We will verify this.

Instead of running this script, we can deploy the contract by ourselves using geth console. This provides a good way to monitor the behaviour of Quorum when handling different types of transaction.

Test of Transactions in Quorum

  1. Deploy a public transaction (every node can see the value)
  2. Deploy a private transaction to one node only

Deploy a Public Transaction

We deploy the contract from Node 1, using eth.accounts[0]. We monitor Node 4 and Node 7.

Image for post
Image for post
Screen split for Node 1, 4 and 7

Deploy the contract in Node 1, with initial value set to 10. Note that no privateFor is set, which means it is a public transaction.

> var bytecode = <bytecode taken from script1.js>
> var simpleContract = eth.contract(<abi taken from script1.js>)
> var test1 = simpleContract.new(10, {from:eth.accounts[0], data: bytecode, gas: 0x47b760})
// to obtain the deployed address
> test1.address
Image for post
Image for post

Here is the deployed address:

“0x1932c48b2bf8102ba33b4a6b545c32236e342f34”

To check the current value, we use test1.get().

Image for post
Image for post

On Node 4 and Node 7, we can define test1 variable pointing to that deployed contract, and check the initial value with test1.get().

> var simpleContract = eth.contract(<abi taken from script1.js>)
> var test1 = simpleContract.at(<deployed address>)
> test1.get()
Image for post
Image for post
Node 4
Image for post
Image for post
Node 7

This is the result we expect. Without privateFor, this is a public transaction, which means all nodes participate in this transaction and can access the deployed contract. And in public transaction, the Transaction Manager on each node is not participating. It is just the normal Ethereum process.

Image for post
Image for post
Public Transaction in Quorum

Let’s set a new value from Node 1, and verify that this value is seen in Node 4 and 7.

Image for post
Image for post
Node 1
Image for post
Image for post
Node 4
Image for post
Image for post
Node 7

To further explore the transaction, we obtain Transaction ID from the deployed contract from Node 1, and get the Transaction Detail.

Image for post
Image for post
Node 1

From Node 4 and Node 7, we need to key in this Transaction Hash in order to get Transaction detail.

Image for post
Image for post
Node 4 and 7

Here we observe that,

  • All three nodes see this transaction.
  • The input field is the exact bytecode when we deploy the contract. That means this data is in clear text, visible in the transaction.
  • The v value is 0x1b (27), which means a public transaction.

Deploy a Private Transaction

Deploy the contract in Node 1

> var test2 = simpleContract.new(20, {from:eth.accounts[0], data: bytecode, gas: 0x47b760, privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]})// to obtain the deployed address
> test2.address
// to get the initial value
> test2.get()
Image for post
Image for post
Node 1

Here is the deployed address:

“0x1349f3e1b8d71effb47b840594ff27da7e603d17”

Now we define test2 variable in both Node 4 and Node 7, and see whether we can get this initial value.

> var test2 = simpleContract.at(<deployed address>)
> test2.get()
Image for post
Image for post
Node 4
Image for post
Image for post
Node 7

This is what we wish to achieve! Node 4 cannot see the value on this deployed contract, and Node 7 can. The reason behind is that it is a private contract, and when deployed, Node 1 has specified only Node 7 in this transaction.

Here is what happens. Node 1 encrypts the payload and sends the encrypted payload and the decryption method to Node 7 (and Node 7 only). This is indexed by the hash of the encrypted payload. This hash replaces the payload in the original transaction. This modified transaction (in yellow box in the diagram) follows the normal ethereum process (p2p to all nodes).

Image for post
Image for post
Private Transaction in Quorum

If we again go into the Transaction detail

Image for post
Image for post
Node 1
Image for post
Image for post
Node 4 and 7

We observe that,

  • This private transaction, as other transactions in Ethereum, is visible to all nodes.
  • The input value is no longer the original payload (compared to the public transaction in the value “0x606060…”). Instead, it is a hash value “0x0e22be…”, and this hash is later used as index if the node is the specific recipient.
  • Therefore, no original payload appears even though every node gets this transaction record.
  • The v value is “0x26” (38), which means a private transaction.

Here is what happens when Node 4 and Node 7 process this transaction.

Node 4 finds nothing in its own Transaction Manager on this hash. Therefore the original payload is not visible in Node 4, and Node 4 does not process this transaction. Node 4 simply ignores this.

Node 7, on the other hand, finds in its own Transaction Manager the encrypted payload indexed by this hash. After proper decryption, Transaction Manager presents the original transaction payload to Quorum Node, and Quorum Node will process this transaction.

Image for post
Image for post

Summary

Again, if you wish to know more about Quorum, here is where you find more in detail.

Written by

Happy to share what I learn on blockchain. Visit http://www.ledgertech.biz/kcarticles.html for my works. or reach me on https://www.linkedin.com/in/ktam1/.

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store