We’ll use Electroneum as our example cryptocurrency. It’s a fairly promising coin from the Monero family. According to its developers, it’s resistant to mining on specialized hardware—in other words, using dedicated rigs would be uneconomical, costing more than the profit you could make. That helps keep the playing field relatively level for all miners. Since it’s based on Monero, much of what’s written here will also hold true for other cryptocurrencies in the same family.
First, let’s clarify what mining is. Essentially, it’s the process of verifying cryptocurrency transactions. There’s no central authority; instead, other participants in the network make sure no one spends the same funds twice or otherwise tries to cheat the system. Miners are rewarded with a small amount of cryptocurrency. That income consists of the reward for creating a new block and the transaction fees, which are paid by users and included in their transactions.
Creating a new block involves solving a specific mathematical problem: finding a block hash that is lower than a network-defined target. This target is called the difficulty. The network adjusts it to keep block creation times reasonably predictable. The first miner to solve the problem receives the full reward. As of today, the block reward is 11,300.93 ETN, which is approximately $146.20.
A block doesn’t have to include other users’ transactions; it can consist of just a single transaction that mints new coins. Why give money away? First, it attracts more participants to the network; second, it lowers the risk of attacks because it’s easier to earn through legitimate means.
To join the Electroneum network, download the software package from the official site. Choose the direct miner for your platform. After downloading and unpacking, you’ll need to sync with the network—this means downloading all previously generated blocks. For development and testing, it’s best to use the testnet with reduced difficulty.
Unfortunately, “out-of-the-box” synchronization may hang at block 155750. This is due to a critical bug that was discovered and the resulting major changes to the Electroneum network (details). So before you start syncing, download the file with the correct blockchain and put it in the . folder. Then run the import:
> ./electroneum-blockchain-import --testnet --verify 0Now go ahead and start the sync:
> ./electroneumd --testnetNext, create a wallet to receive your earnings:
> electoneum-wallet-cli --testnetAfter answering all the prompts, you’ll get your public address in the file <. If you don’t want to spin up an Electroneum server, you can use the public node at nodes..
Time to fire up your favorite editor and start coding. Communication with the Electroneum service uses the JSON-RPC protocol. We’ll only need two methods: fetch a block template and submit a solution. Let’s start with a simple HTTP client:
public String sendRpcCommand(String command) {
// Define the URL for communicating with the server. For the real (non-test) network the port is 26968
URL url = new URL("http://127.0.0.1:34568/json_rpc");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
// Configure the connection. Enable output so we can send the request and read the server’s response
con.setDoOutput(true);
// We'll send data in JSON format
con.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
con.setRequestProperty("Accept", "application/json");
con.setRequestMethod("POST");
// Send the command
OutputStream os = con.getOutputStream();
os.write(command.getBytes("UTF-8"));
os.close();
StringBuilder sb = new StringBuilder();
int HttpResult = con.getResponseCode();
if (HttpResult == HttpURLConnection.HTTP_OK) {
// If the connection is successful, read the server's response
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
br.close();
return sb.toString();
} else {
// If the connection fails, throw an exception with the error description
throw new IOException(con.getResponseMessage());
}
}
To get a block template for computation, send the command
{ "jsonrpc":"2.0", "id":"0", "method":"get_block_template", "params":{ "wallet_address":"44GBHzv6ZyQdJkjqZje6KLZ3xSyN1hBSFAnLP6EAqJtCRVzMzZmeXTC2AHKDS9aEDTRKmo6a6o9r9j86pYfhCWDkKjbtcns", "reserve_size":8 }}For the wallet_address parameter, use the address from the < file. This address is used to immediately generate the block reward transaction. The reserve_size parameter sets how many reserved bytes to allocate, which can later be used during mining. The maximum is 255 bytes.
As a result, we get:
{ "id": "0", "jsonrpc": "2.0", "result": { "blockhashing_blob": "070784e5dbda054486739aac8830906e18272012b97b98993afccf89d0044241193d1788f760cb0000000057754af7e8324054869263b355ede600c2381cbdf6acf2dc8f2b26f4a9a82bae14", "blocktemplate_blob": "070784e5dbda054486739aac8830906e18272012b97b98993afccf89d0044241193d1788f760cb000000000183d71401fff1d61401eff844020117b5d2cead5bd512ab4b0a2e73377049c49c69ffc916687e811bbb0f5f65322b01d67fec53c3f1cab976537a4ab4ebba03c89849d554963df6ed1a0023e6d5d9e90208000000000000000013d5c0b347172631f9b0175365936f98b00198d8caf3dbb77edc6c002dbb6c302776e7d543da92fdf5c30e91d4b21762eb6fe5daf8959b519f3de65a3cd80adda1e5674fedeb2a5038577ea2fe9eb6a3fd2162a3a09cbe6d3b62c9b04a29d47c5c14c119f0812448ab4e14a76f1c2ddc2ff6ac0b97f1fb9e4cabf0ef2adf79221a3e865b8d9252f41f31e110326b78b0c506e9f18eb094305b6216221c2bd3f9d996bedf54dbb4c0bfe4fea6f2240181c91789270a48cae44d7662e1a13aae45c3edc3247736879f6aa2670b8816e551856b912f11269979fac1c97203365247eaee476ed815e3fa597b5230db7e0162816b55b23d2bfb8b9506492e8359f8ba33807eab0972a7837893163cadf314888dbb64190fa00553156dc7b05574eacd3b9a268666201ab202b23ecf960565c01a6a61fe5f03ba5b6c22d7e6639e7708941c876ecdc191cec4c5797e520855d9cc34ef9c3866ded9a4722c6437363bb7a47c9dbd303c15a18dfb72028054cd438924978f5c5d32be3bcbc622e0fb4b9aef865fea52a09f518952ec0aa94bbfa969f192a93b80a50fe7af2728cbd76e739e9af80aee2644fb2bbe1c82724bdc4678a5a206a945a3e49dabcb10ae0f25d473aa76e0275c4f9fa1cffc3e1d8748278561b99953966606a5d891717b4fb0366a77e38db4c267c3724e994532ae97fc7b12842157d8a11bc97926eb9978c82a07afc573a04660247a94c5c4f14556fbcc9aa367b7bef4fdf18b626b4342d4e84850f133076dcd26c16d3efe9f85fa29c757acda5dff2fe26fbf87d937be455d4053e4246a3055ace5fcb6d6545aa3cd0b2e21ea3648f0dd6cde386933381b7116", "difficulty": 237219196877, "expected_reward": 1129583, "height": 338801, "prev_hash": "4486739aac8830906e18272012b97b98993afccf89d0044241193d1788f760cb", "reserved_offset": 126, "status": "OK" }}Let’s take a closer look at the first two fields.


Electroneum offers two ways to mine. One option is to use the prebuilt blockhashing_blob and brute-force the four-byte nonce. The upside is you don’t have to compute the transactions’ Merkle root yourself. The downside is the search space is quite small, so you might never hit a valid solution.
The second option is to use the raw blocktemplate_blob. In this mode you can iterate over both the 4-byte nonce and the block’s extra data field, which greatly increases the search space. The downside is you must first compute the hash of the first transaction and the Merkle root, and only then compute the hash of the block itself.
To start, let’s try the first approach. We’ll write a small method that iterates through nonce values.
public static boolean findProperNonce(byte[] blockheader, int nonceByteIndex, long difficulty) {
byte nonceByte = Byte.MIN_VALUE;
while (nonceByte != Byte.MAX_VALUE) {
blockheader[39 + nonceByteIndex] = nonceByte;
if (nonceByteIndex < 3) {
boolean found = findProperNonce(blockheader, nonceByteIndex + 1, difficulty);
if (found) {
return true;
}
} else {
byte[] hash = calculateHash(blockheader);
if (hasRequiredDifficulty(hash, difficulty)) return true;
}
nonceByte++;
}
return false;
}
Electroneum uses the CryptoNight hashing algorithm. You can find a description of the algorithm here. The good news is there are plenty of ready-made implementations; the bad news is that almost all of them are written in C. Fortunately, the JVM can call native C code. So, to save time, we’ll take an existing implementation and build a DLL (shared library) for our miner.
For this, we’ll need Cygwin. It’s a collection of open-source Linux utilities that run on Windows. During installation, select the packages mingw64-x86_64-gcc-core and mingw64-x86_64-gcc-g++.

To load the library, create a CryptoNight class in the com. package.
public class Cryptonight {
// Load the library named minerhashing; no need to specify the extension
static {
System.loadLibrary("minerhashing");
}
// Hash calculation method from the library
public native static void calculateHash(byte[] output, byte[] input);
}
The calculateHash method is declared as native, which means it’s implemented in another language. Next, we need to generate the header file:
> %JAVA_HOME%\bin\javah.exe -jni -v -d com/gogaworm/electroneumminer com.gogaworm.electroneumminer.CryptonightWe’ll get a header file com_gogaworm_electroneumminer_Cryptonight. with the method Java_com_gogaworm_electroneumminer_Cryptonight_hash, which needs to be implemented in C. To do this, create a file with the same name but with a .c extension. Move both files into the libcryptonight source directory.
// Make sure to include the JNI header file. It is located in the installed Java directory#include <jni.h>JNIEXPORT void JNICALL Java_com_gogaworm_electroneumminer_Cryptonight_calculateHash (JNIEnv *env, jclass clazz, jbyteArray output, jbyteArray input) { // Copy the data arrays into a new memory region. You cannot work with arrays directly in the Java heap because the GC may move them in memory unsigned char* inputBuffer = (*env)->GetByteArrayElements(env, input, NULL); unsigned char* outputBuffer = (*env)->GetByteArrayElements(env, output, NULL); // Determine the array sizes jsize inputSize = (*env)->GetArrayLength(env, input); jsize outputSize = (*env)->GetArrayLength(env, output); // Calculate the hash cryptonight_hash(outputBuffer, inputBuffer, inputSize); // Release the memory used for the arrays (*env)->ReleaseByteArrayElements(env, input, inputBuffer, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, output, outputBuffer, JNI_COMMIT);}Now open a Cygwin shell and build the DLL:
> x86_64-w64-mingw32-gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/win32" -shared -o minerhashing.dll -g com_gogaworm_electroneumminer_Cryptonight.c cryptonight.c crypto/aesb.c crypto/c_blake256.c crypto/c_groestl.c crypto/c_jh.c crypto/c_keccak.c crypto/c_skein.c crypto/oaes_lib.cFor our miner to detect the library, set the Java system property java..
Let’s verify that the library works correctly. The document describing the CryptoNight algorithm includes two test vectors. We’ll run one of them:
@Test
public void testHashMethod() throws UnsupportedEncodingException {
byte[] outputBuffer = new byte[32];
byte[] input = hexStringToByteArray(block);
Cryptonight.calculateHash(outputBuffer, "This is a test".getBytes("US-ASCII"));
assertEquals("a084f01d1437a09c6985401b60d43554ae105802c5f5d8a9b3253649c0be6605", bytesToHex(outputBuffer).toLowerCase());
}
What’s left is a way to check whether we’ve found a valid value. The get_block_template command returns a difficulty parameter, which indicates how hard it is to find a valid hash. According to the specification (https://cryptonote.org/cns/cns010.txt), difficulty = (2^265 – 1) / target. For this formula, the block hash must be converted from big-endian to little-endian (https://en.wikipedia.org/wiki/Endianness). Then we compare it to the current difficulty to see whether the value qualifies:
public static boolean hasRequiredDifficulty(byte[] hash, BigInteger difficulty) {
BigInteger diff1 = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16);
BigInteger reversed = new BigInteger(bytesToHex(Arrays.reverse(hash)), 16);
BigInteger hashdiff = diff1.divide(difficulty);
if (hashdiff.compareTo(difficulty) >= 0) {
return true;
}
return false;
}
To verify that the method works correctly, let’s test it on an existing block from the network. You can fetch it with the getblock command. We’ll take the block at height 338,401. Its hash is
13b3cf8b04b6bb78f0c7c1a50f7e8656963c1f48a56ba89999eddf0531750b15
and the difficulty is 252087628780. As a result of the calculations, we find that the hashdiff is greater than the difficulty.
Once you’ve found the correct nonce, you can broadcast the block to the network. This is done with the command
{ "jsonrpc":"2.0", "id":"0", "method":"submitblock", "params":{ "Block ":"blob template with the required nonce" }}All that’s left is to move the server-handling and mining routines into separate threads, and the basic miner is ready.
Before we wrap up
According to the Electroneum developers, you can even mine their cryptocurrency on smartphones. A mining app is already on Google Play. In reality, though, it’s just a simulation: instead of solving a hard cryptographic puzzle, it measures the available CPU performance that could theoretically be used for mining and credits earnings based on that score. So a real Android miner would look somewhat different.
But that’s another story entirely.