How to write your own blockchain with 120 lines of Java code

Time:2020-2-9

Blockchain is the most popular topic at present. Most readers have heard about bitcoin and perhaps smart contract. I believe everyone would like to know how it works. This article is to help you use the Java language to implement a simple blockchain, with less than 120 lines of code to reveal the principle of blockchain!

“You can implement your own blockchain in less than 120 lines of Java code!” Sounds incredible, right? What is a better way to learn and practice than to develop your own blockchain? Then let’s practice together!

Because we are a technology company engaged in Internet finance, we use the amount of virtual assets as the sample data in this article. You can think of a number for yourself first, which we will use later.

Through this article, you will be able to:

1. Create your own blockchain

2. Understand how hash functions maintain the integrity of blockchain

3. How to create and add new blocks

4. How multiple nodes compete to generate blocks

5. View the entire chain through a browser

6. All other basic knowledge about blockchain

However, consensus algorithms such as POW and POS will not be covered in this paper.

At the same time, in order to let you see more clearly the blockchain and the addition of blocks, we have simplified the process of network interaction. P2P networks such as “peer-to-peer networks” will be explained in future articles.

Let’s start!

Set up

Let’s assume you have a little experience in Java language development and Maven project construction. After installing and configuring the java development environment, we create a new Maven project and add some dependencies in POM:

<! -- subminiature Web Framework -- > 
    <dependency> 
      <groupId>com.sparkjava</groupId> 
      <artifactId>spark-core</artifactId> 
      <version>${spark.version}</version> 
    </dependency>

Spark web framework is a very small framework based on jetty. We use it to write HTTP access request processing.


<dependency> 
      <groupId>commons-codec</groupId> 
      <artifactId>commons-codec</artifactId> 
      <version>${commons.codec.version}</version> 
    </dependency> 

This general package has almost all encryption and decryption algorithms and conventional operations


<dependency> 
      <groupId>com.google.code.gson</groupId> 
      <artifactId>gson</artifactId> 
      <version>2.8.2</version> 
    </dependency> 

Google’s JSON package, of course, you can use other JSON packages you like.

Finally, add log related packages


<!-- log start --> 
    <dependency> 
      <groupId>log4j</groupId> 
      <artifactId>log4j</artifactId> 
      <version>${log4j.version}</version> 
    </dependency> 
    <dependency> 
      <groupId>org.slf4j</groupId> 
      <artifactId>slf4j-api</artifactId> 
      <version>${slf4j.version}</version> 
    </dependency> 
    <dependency> 
      <groupId>org.slf4j</groupId> 
      <artifactId>slf4j-log4j12</artifactId> 
      <version>${slf4j.version}</version> 
    </dependency> 
    <!-- log end --> 

Related version property settings


<properties> 
    <commons.codec.version>1.9</commons.codec.version> 
    <spark.version>2.6.0</spark.version> 
    <slf4j.version>1.6.6</slf4j.version> 
    <log4j.version>1.2.17</log4j.version> 
    <gson.version>2.8.2</gson.version> 
</properties> 

Next, we create a sparkweb. Java file. After that, most of our work is around this file, let me start coding it!

data model

Let’s define a block class, which represents the data model of each block of the blockchain:

public class Block { 
  /**It's the position of this block in the whole chain*/ 
  private int index; 
  /**It's obviously the time stamp when the block was generated*/ 
  private String timestamp; 
  /**Virtual assets. Data we need to record*/ 
  private int vac; 
  /**Is the hash value generated by this block through sha256 algorithm*/ 
  private String hash; 
  /**Sha256 hash value pointing to the previous block*/ 
  private String prevHash; 
  /** getters and setters**/  
}

Next, we define a structure to represent the whole chain. The simplest representation is a block sequence table:


ArrayList<Block> blockChain 

We use the hash algorithm (sha256) to determine and maintain the correct order of blocks and blocks in the chain, ensuring that the prevhash value of each block is equal to the hash value of the previous block, so as to build the chain in the correct block order:

[ index:0| hash:”xxxw”| preHash:””] – [ index:1| hash:”xxxx”| preHash:”xxxw”] – [ index2| hash:”xxxy”| preHash:”xxxx”]

Hashing and building blocks

Why do we need hashing? There are two main reasons:

1. Uniquely identify data on the premise of space saving. Hash is calculated from the data of the whole block. In our example, the data of the whole block is calculated into a fixed length and non forgeable string through sha256.

2. Maintain chain integrity. By storing the hash value of the previous block, we can ensure the correct order of each block in the chain. Any tampering with the data will change the hash value and destroy the chain at the same time. Take the medical and health field we are engaged in as an example. For example, if a malicious third party modifies the VAC value in one or several blocks to adjust the price of “life insurance”, then the whole chain becomes untrustworthy.

We then write a function to calculate the sha256 hash value of the given data:


public static String calculateHash(Block block) { 
    String record = (block.getIndex()) + block.getTimestamp() + (block.getVac()) + block.getPrevHash(); 
    return SHA256.crypt(record); 
} 

Next, we can get a function to generate blocks:


public static Block generateBlock(Block oldBlock, int vac) { 
    Block newnewBlock = new Block(); 
    newBlock.setIndex(oldBlock.getIndex() + 1); 
    newBlock.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); 
    newBlock.setVac(vac); 
    newBlock.setPrevHash(oldBlock.getHash()); 
    newBlock.setHash(calculateHash(newBlock)); 
    return newBlock; 
} 

The index is obtained from the index increment of the given previous block, the timestamp is obtained directly through the new date() function, the hash value is calculated by the calculatehash function, and prevhash is the hash value of the given previous block.

Check block

After that, we need to have a function to judge whether a block has been tampered with. Check the index to see whether the block is incrementing correctly, check whether prevhash is consistent with the previous block’s hash, and then check whether the current block’s hash value is correct through calculatehash. In these steps, we can write a verification function:


public static boolean isBlockValid(Block newBlock, Block oldBlock) { 
    if (oldBlock.getIndex() + 1 != newBlock.getIndex()) { 
      return false; 
    } 
    if (!oldBlock.getHash().equals(newBlock.getPrevHash())) { 
      return false; 
    } 
    if (!calculateHash(newBlock).equals(newBlock.getHash())) { 
      return false; 
    } 
    return true; 
  } 

In addition to the check block, we will encounter a problem: both nodes generate blocks and add them to their chains. Who should we take as the criterion? Let’s leave the details here for the next article. Let’s remember one principle: always choose the longest chain.

[block 1] – > [block 2] – > [block 3] – > [block 4] – > [block 5] – > approval

[block 1] – > [block 2] – > [block 3] – > [block 4] – > discard

Generally speaking, a longer chain means that its data (state) is more new, so we need a function to help us switch the local expired chain to the latest one:


public void replaceChain(ArrayList<Block> newBlocks) { 
    if (newBlocks.size() > blockChain.size()) { 
      blockChain = newBlocks; 
    } 
  } 

At this stage, we have basically completed all the important functions. Next, we need a convenient and intuitive way to view our chain, including data and status. Viewing web pages through a browser may be the most appropriate way.

Web services

I guess you must be very familiar with traditional web services and development, so you will definitely see this part.

With the spark web framework, we can complete our web services as follows:

public static void main(String[] args) { 
    //Port (5678); // the default port is 4567. You can set other ports  
}

OK, finish, right, you’re right. It’s just an empty main method.

Next, we define different endpoints and corresponding handlers. For example, we can view the whole chain for get request of “/”, and create a new block for post request of “/”.

Handler for get request:


get("/", (q, a) ->{return gson.toJson(blockChain)}); 

To simplify, we return the whole chain directly in JSON format. You can visit localhost: 4567 or 127.0.0.1:4567 in the browser to check it

The handler of post request is a little complicated. Let’s define the payload of post request first:


public class Message { 
  private int vac; 
//getters and setters 
} 

Let’s look at the implementation of handler:


post("/", (q, a) -> { 
String body = request.body(); 
       Message m = gson.fromJson(body, Message.class); 
       if (m == null) { 
         return "vac is NULL"; 
       } 
       int vac = m.getVac(); 
       Block lastBlock = blockChain.get(blockChain.size() - 1); 
       Block newBlock = generateBlock(lastBlock, vac); 
       if (isBlockValid(newBlock, lastBlock)) { 
         blockChain.add(newBlock); 
         LOGGER.debug(gson.toJson(blockChain)); 
       } else { 
         return "HTTP 500: Invalid Block Error"; 
       } 
       return "success!"; 
}); 

The payload defined above can be used in our post request body, for example:

{"vac":7500}

Remember the generateblock function we wrote earlier? It takes a “previous block” parameter and a VAC value. After receiving the request, the post handler can get the VAC value in the request body, and then generate a new block with the function of generating block and verifying block!

In addition, you can:

1. Using the function of new gsonbuilder(). Setprettyprinting(). Create(), you can print the data JSON in the console in a very beautiful and easy to read way, which is convenient for debugging.

2. When testing post requests, you can use the chrome plug-in of postman, which is more intuitive and convenient than curl. You can also use restclient, the Firefox plug-in.

It’s almost done

Next, we “assemble” these functions related to blockchain and Web Services: most importantly, we need to generate the first block (creation block) as the head of blockchain.

// creation block 
    Block genesisBlock = new Block(); 
    genesisBlock.setIndex(0); 
    genesisBlock.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); 
    genesisBlock.setVac(0); 
    genesisBlock.setPrevHash(""); 
    genesisBlock.setHash(calculateHash(genesisBlock)); 
    blockChain.add(genesisBlock);

The genesis block is the most important part of the main function. It is used to initialize the blockchain. After all, there must be a beginning. Prevhash of the first block is empty.

Oh yeah! It’s done

Let’s start it:

In the terminal, we can see the log information of web server startup, and print out the information of creation block:

[INFO ] 2018-02-08 10:58:26 [email protected](SparkWeb.java:132):[ 

“index”: 0, 
“timestamp”: “2018-02-08 10:58:25”, 
“vac”: 0, 
“hash”: “7c2d2db62a82ac8aa3d843ff837c604d8bd17800f4c466d472c5df185b8967fa”, 
“prevHash”: “” 


[INFO ] 2018-02-08 10:58:26 [email protected](Log.java:192):Logging initialized @1267ms to org.eclipse.jetty.util.log.Slf4jLog 
[INFO ] 2018-02-08 10:58:26 [email protected](EmbeddedJettyServer.java:127):== Spark has ignited … 
[INFO ] 2018-02-08 10:58:26 [email protected](EmbeddedJettyServer.java:128):>> Listening on 0.0.0.0:4567 
[INFO ] 2018-02-08 10:58:26 [email protected](Server.java:372):jetty-9.4.4.v20170414 
[INFO ] 2018-02-08 10:58:26 [email protected](DefaultSessionIdManager.java:364):DefaultSessionIdManager workerName=node0 
[INFO ] 2018-02-08 10:58:26 [email protected](DefaultSessionIdManager.java:369):No SessionScavenger set, using defaults 
[INFO ] 2018-02-08 10:58:26 [email protected](HouseKeeper.java:149):Scavenging every 600000ms
 [INFO ] 2018-02-08 10:58:27 [email protected](AbstractConnector.java:280):Started [email protected]{HTTP/1.1,[http/1.1]}{0.0.0.0:4567} 
[INFO ] 2018-02-08 10:58:27 [email protected](Server.java:444):Started @1669ms

Then we open the browser and visit the address http: / / localhost: 4567. We can see that the current information of the whole blockchain is displayed in the page (of course, there is only one creation block at present):


{ 
"index": 0, 
"timestamp": "2018-02-08 10:58:25", 
"vac": 0, 
"hash": "7c2d2db62a82ac8aa3d843ff837c604d8bd17800f4c466d472c5df185b8967fa", 
"prevHash": "" 
} 

Next, we send some post requests through resrclient: Post http: / / localhost: 4567 / {“VAC”: 15} [send];

Or use the curl command: curl – x post – I http: / / localhost: 4567 / — data ‘{“VAC”: 125}’.

Refresh the http: / / localhost: 4567 page just now. Now there is an additional block in the chain, which is exactly what we just generated. At the same time, we can see that the order and hash value of the blocks are correct.

Source: https://github.com/mignet/blockchain

The above is the whole content of this article. I hope it will help you in your study, and I hope you can support developepaer more.