Implementing a Simple Blockchain in Java with Web API Endpoints
This tutorial walks through building a simple blockchain in Java, covering core concepts, block and transaction structures, proof‑of‑work implementation, and exposing API endpoints via Servlets for creating transactions, mining blocks, viewing the chain, and achieving consensus across nodes.
This article provides a step‑by‑step guide to creating a functional blockchain using Java, starting from basic preparation and environment setup to full implementation of core blockchain features and network consensus.
Preparation
The reader should be comfortable with Java SE, Java Web (Servlet) development, and HTTP protocol basics. Required tools include JDK 1.8, Tomcat 9, Maven 3.5, and a JSON library.
Environment Description
The Maven pom.xml includes dependencies for javaee-api and json :
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
</dependencies>Blockchain Core Class
The BlockChain class stores the chain and the list of pending transactions. It provides methods to create the genesis block, add new blocks, retrieve the last block, and manage transactions.
package org.zero01.core;
import java.util.*;
public class BlockChain {
private List
> chain;
private List
> currentTransactions;
private static BlockChain instance = null;
private BlockChain() {
chain = new ArrayList<>();
currentTransactions = new ArrayList<>();
newBlock(100, "0"); // genesis block
}
public static BlockChain getInstance() {
if (instance == null) {
synchronized (BlockChain.class) {
if (instance == null) {
instance = new BlockChain();
}
}
}
return instance;
}
public Map
newBlock(long proof, String previousHash) {
Map
block = new HashMap<>();
block.put("index", chain.size() + 1);
block.put("timestamp", System.currentTimeMillis());
block.put("transactions", currentTransactions);
block.put("proof", proof);
block.put("previous_hash", previousHash != null ? previousHash : hash(chain.get(chain.size() - 1)));
currentTransactions = new ArrayList<>();
chain.add(block);
return block;
}
public int newTransactions(String sender, String recipient, long amount) {
Map
tx = new HashMap<>();
tx.put("sender", sender);
tx.put("recipient", recipient);
tx.put("amount", amount);
currentTransactions.add(tx);
return (Integer) lastBlock().get("index") + 1;
}
public Map
lastBlock() {
return chain.get(chain.size() - 1);
}
public static Object hash(Map
block) {
return new Encrypt().getSHA256(new JSONObject(block).toString());
}
// Proof‑of‑Work methods are added later
}Block Structure
Each block contains an index , timestamp , a list of transactions , a proof (nonce), and the previous_hash . The hash links blocks together, ensuring immutability.
Hash Utility
The Encrypt class provides SHA‑256, SHA‑512, and MD5 hashing utilities used for block hashing and proof‑of‑work validation.
package org.zero01.util;
import java.security.*;
public class Encrypt {
public String getSHA256(final String text) { return SHA(text, "SHA-256"); }
public String getSHA512(final String text) { return SHA(text, "SHA-512"); }
public String getMD5(final String text) { return SHA(text, "MD5"); }
private String SHA(final String text, final String type) {
// implementation omitted for brevity
}
}Proof‑of‑Work
A simple PoW algorithm searches for a number p' such that hash(last_proof + p') starts with four zeros. The methods proofOfWork and validProof implement this logic.
public long proofOfWork(long lastProof) {
long proof = 0;
while (!validProof(lastProof, proof)) {
proof++;
}
return proof;
}
public boolean validProof(long lastProof, long proof) {
String guess = lastProof + "" + proof;
String guessHash = new Encrypt().getSHA256(guess);
return guessHash.startsWith("0000");
}Servlet API
Three Servlets expose the blockchain functionality over HTTP:
/transactions/new – accepts a JSON payload with sender , recipient , and amount and adds a pending transaction.
/mine – runs the PoW algorithm, rewards the miner with a new transaction (sender "0"), creates a new block, and returns the block data.
/chain – returns the full chain and its length as JSON.
Example of the NewTransaction servlet handling POST data:
@WebServlet("/transactions/new")
public class NewTransaction extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader reader = req.getReader();
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
JSONObject json = new JSONObject(sb.toString());
// validate required fields
String[] required = {"sender", "recipient", "amount"};
for (String f : required) {
if (!json.has(f)) {
resp.sendError(400, "Missing values");
return;
}
}
BlockChain bc = BlockChain.getInstance();
int index = bc.newTransactions(json.getString("sender"), json.getString("recipient"), json.getLong("amount"));
resp.setContentType("application/json");
resp.getWriter().println(new JSONObject().put("message", "Transaction will be added to Block " + index));
}
}The Mine servlet demonstrates mining and rewarding the node using a UUID stored in the servlet context:
@WebServlet("/mine")
public class Mine extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BlockChain bc = BlockChain.getInstance();
long lastProof = Long.parseLong(bc.lastBlock().get("proof").toString());
long proof = bc.proofOfWork(lastProof);
String uuid = (String) getServletContext().getAttribute("uuid");
bc.newTransactions("0", uuid, 1); // reward
Map
newBlock = bc.newBlock(proof, null);
JSONObject response = new JSONObject();
response.put("message", "New Block Forged");
response.put("index", newBlock.get("index"));
response.put("transactions", newBlock.get("transactions"));
response.put("proof", newBlock.get("proof"));
response.put("previous_hash", newBlock.get("previous_hash"));
resp.setContentType("application/json");
resp.getWriter().println(response);
}
}Consensus and Node Registration
To turn the single‑node prototype into a distributed network, the BlockChain class now holds a Set nodes . Nodes can be registered via a /nodes/register servlet that calls registerNode(address) . The resolveConflicts() method fetches the /chain from each neighbor, validates each chain with validChain() , and adopts the longest valid chain.
public boolean validChain(List
> chain) {
Map
lastBlock = chain.get(0);
int currentIndex = 1;
while (currentIndex < chain.size()) {
Map
block = chain.get(currentIndex);
// check hash linkage
if (!block.get("previous_hash").equals(hash(lastBlock))) {
return false;
}
// check proof of work
long lastProof = Long.parseLong(lastBlock.get("proof").toString());
long proof = Long.parseLong(block.get("proof").toString());
if (!validProof(lastProof, proof)) {
return false;
}
lastBlock = block;
currentIndex++;
}
return true;
}
public boolean resolveConflicts() throws IOException {
Set
neighbours = this.nodes;
List
> newChain = null;
int maxLength = this.chain.size();
for (String node : neighbours) {
URL url = new URL("http://" + node + "/chain");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
if (conn.getResponseCode() == 200) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
}
JSONObject json = new JSONObject(sb.toString());
int length = json.getInt("length");
List
> chain = (List) json.getJSONArray("chain").toList();
if (length > maxLength && validChain(chain)) {
maxLength = length;
newChain = chain;
}
}
}
if (newChain != null) {
this.chain = newChain;
return true;
}
return false;
}Two additional servlets, RegisterNode and ResolveNode , expose these capabilities over HTTP, allowing multiple Tomcat instances (e.g., on ports 8089 and 8066) to discover each other, mine independently, and achieve consensus.
Demonstration
The article concludes with a walkthrough of using Postman to add transactions, mine blocks, register nodes, and invoke the consensus endpoint, showing that after resolution both nodes share the same longest chain.
Overall, the tutorial demonstrates a complete, runnable blockchain implementation in Java, covering data structures, cryptographic hashing, proof‑of‑work, RESTful APIs, and a basic consensus algorithm.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.