Since quite a lot of business applications are written in Java, or make use of the JVM for that matter, I believe a primer on how to interface with an Ethereum Smart Contract in Java will prove helpful to the reader.
This post requires knowledge of what a Smart Contract is and how to deploy one.
To learn why we will probably use a library for interfacing with the Smart Contract a basic understanding of the usage of JSON-RPC in Ethereum is necessary.
Let’s unveil the supposed magic behind what almost all Ethereum libraries do to interact with the blockchain. Suppose we need to know the gasLimit
(maximum amount of computational effort the transactions in a block are allowed to have) of the latest block. Using curl and piping its result to a JSON parser such as jq would, considering we run an Ethereum node on localhost port 8545, look as follows
The above will yield "0x4c4b3c"
, which is hex-encoded. Since we probably want to read it as a decimal we can add another pipe and the final command becomes
Leading to the readable result of 4999996
.
These steps involved are a good showcase on why to introduce an abstraction. Almost all libraries will provide convenient interfaces for these RPC calls.
Using the library web3j the command above will translate to
Still a lot of steps, and from a software-engineering perspective a violation of the Law of Demeter, yet less error prone and more convenient. Additionally, if our implementation depends on the functionality above we will most likely create an abstraction such as ethereumGateway.currentGasLimit()
anyway.
Let’s take web3j for a spin to interact with a simple record keeping contract. What the contract does is keep track of some kind of Deliverable
, which could be a shipping container. It makes use of access controls to only allow the owner of the contract to modify its state. We achieve this with the creation of a onlyByOwner
function modifier. The owner can store
a new Deliverable and also change its status to delivered
. Using the shipping container example this means the container has reached is destination. Querying the contract for the status of a Deliverable
is possible by anyone using the statusFor
function. If the status
of a Deliverable
is 1 we can consider it delivered.
Using the web3j Command Line Tools we need to provide the hex-encoded binary and the application binary interface (ABI) files, an output folder and the package name.
It will create a .java
file with the following contents. Some parts are left for the sake of brevity.
We spot two methods to write to the contract, store
and delivered
, as in they return a TransactionReceipt
. There is one method to query the contract for the status of a Deliverable
via statusFor
, which does return the queried value without the need for a transaction. It even uses the proper type Uint256
. Every lover of type systems will feel right at home.
A sample interaction with the contract can be shown as an integration test
Let’s di-sect the code
We load the contract by providing its address
an instance of web3j
, the credentials
(an unlocked wallet), the gasPrice
and gasLimit
.
Be aware that credentials
, gasPrice
or gasLimit
would not really be necessary if we only intended to read from the contract. For example when using the statusFor
function. Query operations on a contract are free in as they do not need a transaction and thus have no gas cost. The generated method requires these arguments though.
We create a hash of something unique, in our example it could be the shipping receipt, using SHA-3.
Keep in mind we probably do not want to store full documents on the blockchain, because we would need pay for every byte in a transaction. Using hashes is a way to keep the transactions small and cheap.
Next up we create the transaction to store the Deliverable
on the blockchain and wait for the computation to complete.
Almost done. We change the status of the Deliverable
to delivered and, again, wait for the computation to complete.
Finally we query the contract for the state and verify our Deliverable
has been delivered.
Having had a lot more experience with web3.js the original library in JavaScript it sometimes feels more tedious to work with it’s sister library in Java.
As an example, if we want to use our account in web3.js we would write
In web3j we would have to read our wallet file and pass the Credentials
instance around
Regardless, in the end it boils down to personal taste. Of course web3.js is older, more mature and has more users than web3j, thus in the future web3j will improve too in every aspect.
There’s also ethereumJ, a Java implementation of the Ethereum protocol, meaning it includes mining capabilities and an implementation of the blockchain. web3j, calling itself lightweight, does not include these functionalities since they are not necessary for every user wanting to interface with smart contracts.
As a side note, when thinking about the future of our application we should create an abstraction for any library or API we use. It will allow us to switch out the underlying implementation, without touching the modules containing the business logic, should we ever find a better way or library to interact with a smart contract. Furthermore it will enable us to use a somewhat different implementation of Smart Contracts such as the possible competitor on the Bitcoin blockchain Rootstock or any other future Smart Contract ecosystem. Maybe even a boring database or both.
The post has been cross-posted on Medium