본문 바로가기

BlockChain

Java로 배워보는 BlockChain

해당 내용은 공부하면서 간단하게 만들어본 블록체인 입니다.


1. Block 만들기

 - BlockChain을 구성하는 Block 을 생성한다,

 - Block은 BlockChain 내에서 고유한 Hash(Digital Signature)값을 가지고 있다.

 - Block 내에는 이전 Block의 Hash 값과 데이터들이 포함되어 있다.


 1) 소스코드

package kr.geun.o.bc.basic;

import java.util.Date;

/**
* Block Class
*
* @author akageun
*/
public class Block {

private String hash;
private String previousHash;

private String data;
private long timeStamp;

private Block() {

}

/**
* Block Constructor
*
* @param data
* @param previousHash
*/
public Block(String data, String previousHash) {
this.data = data;
this.previousHash = previousHash;

this.timeStamp = new Date().getTime();
}

/**
* get previousHash
*
* @return
*/
public String getPreviousHash() {
return previousHash;
}

/**
* get Hash
* - digital signature
*
* @return
*/
public String getHash() {
return hash;
}

/**
* get TimeStamp
*
* @return
*/
public long getTimeStamp() {
return timeStamp;
}

/**
* get Data
*
* @return
*/
public String getData() {
return data;
}
}

2. Hash(Digital Signature) 생성 메소드 만들기

 - Sha256을 사용.

package kr.geun.o.bc.basic;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* BlockChain Utils
*
* @author akageun
*/
public class BcUtils {

/**
* Hash 생성
*
* @param inputValues
* @return
*/
public static String generateHash(String... inputValues) {
try {
StringBuffer sb = new StringBuffer();
for (String inputValue : inputValues) {
sb.append(inputValue);
}

String input = sb.toString();

MessageDigest digest = MessageDigest.getInstance("SHA-256");

byte[] hash = digest.digest(input.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();

for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}

hexString.append(hex);
}
return hexString.toString();

} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException(e);

} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}


3. Hash 계산로직 추가.

 - 위 BcUtils 를 이용하여 Block 안 Hash값을 만드는 로직을 추가해보자.

 - Block 내 메소드 추가(소스코드)

/**
* Hash값 계산하기!
*
* @return
*/
public String calculateHash() {
return BcUtils.generateHash(previousHash, Long.toString(timeStamp), data);
}

 - 생성자 내에 해당 메소드 호출 추가


/**
* Block Constructor
*
* @param data
* @param previousHash
*/
public Block(String data, String previousHash) {
this.data = data;
this.previousHash = previousHash;

this.timeStamp = new Date().getTime();
this.hash = calculateHash(); //추가!!
}


4. Genesis Block 호출하기!

 - 첫번째 블럭 생성

Block genesisBlock = new Block("이건 Genesis Block 입니다.", "0");
System.out.println("Genesis Block Hash : " + genesisBlock.getHash());

 - 메인 메소드를 만들어 호출하면 아래와 같은 결과를 얻을 수 있다.(호출할 때마다 Hash값이 달라진다.)

Genesis Block Hash : f57e2c72feba62234118d1356799c367a802c342156a43ac2774698bb316941e


 - 몇개를 더 추가해서 출력해보면.... 아래와 같다.

Block genesisBlock = new Block("이건 Genesis Block 입니다.", "0");
System.out.println("Genesis Block Hash : " + genesisBlock.getHash());

Block secBlock = new Block("이건 두번째 블럭 입니다.", genesisBlock.getHash());
System.out.println("Second Block Hash : " + secBlock.getHash());

Block thirdBlock = new Block("이건 세번째 블럭 입니다.", secBlock.getHash());
System.out.println("Hash for block 3 : " + thirdBlock.getHash());

Genesis Block Hash : f57e2c72feba62234118d1356799c367a802c342156a43ac2774698bb316941e

Second Block Hash : 082902f4d78eab658b964d9743532178073da2b9d529b00752fa5588034fb2a2

Hash for block 3 : 97a821109da7ed12fc3263452670d42b392d71772dd70b81720a23e1d898bf15


5. 블록체인 만들기.

 - 간단하게 static List를 만들어서 블럭들을 생성해 보자.

/**
* BlockChain 메인 클래스
*
* @author akageun
*/
public class BlockChain {

public static List<Block> BLOCK_CHAIN = new LinkedList<>();

public static void main(String[] args) {

BLOCK_CHAIN.add(new Block("이건 Genesis Block 입니다.", "0"));
BLOCK_CHAIN.add(new Block("이건 두번째 블럭 입니다.", BLOCK_CHAIN.get(BLOCK_CHAIN.size() - 1).getHash()));
BLOCK_CHAIN.add(new Block("이건 세번째 블럭 입니다.", BLOCK_CHAIN.get(BLOCK_CHAIN.size() - 1).getHash()));

for (Block block : BLOCK_CHAIN) {
System.out.println("=========");

System.out.println("hash : " + block.getHash());
System.out.println("previousHash : " + block.getPreviousHash());
System.out.println("timeStamp : " + block.getTimeStamp());
System.out.println("Data : " + block.getData());
}

System.out.println("=========");
}

}

 - 결과

=========

hash : 0f7e40c1f99be348a2a600f678ef6e64136d4ea6dc3d7457561e572129c628c9

previousHash : 0

timeStamp : 1532238023070

Data : 이건 Genesis Block 입니다.

=========

hash : b3a2226d4dafb77bfca41c2d792e7590df544e34450bfd5ecd69e71124f6baea

previousHash : 0f7e40c1f99be348a2a600f678ef6e64136d4ea6dc3d7457561e572129c628c9

timeStamp : 1532238023086

Data : 이건 두번째 블럭 입니다.

=========

hash : 57caa0507cbed37fbde89a4d80b2ea68450cff3698fba56856858093d8ac6982

previousHash : b3a2226d4dafb77bfca41c2d792e7590df544e34450bfd5ecd69e71124f6baea

timeStamp : 1532238023086

Data : 이건 세번째 블럭 입니다.



6. 무결성 검사

 - 계산한 Hash값이 같은지, Block 내 PreviousBlock 과 실제 이전 블럭 Hash값이 같은지를 검증하는 로직

 - 구현 소스

/**
* 블록 검증
*
*
* @return
*/
private static boolean isValidBlockChain() {
Block currentBlock;
Block previousBlock;

for (int i = 1; i < BLOCK_CHAIN.size(); i++) { //GenesisBlock 은 제외한다.
currentBlock = BLOCK_CHAIN.get(i);
previousBlock = BLOCK_CHAIN.get(i - 1);

if (currentBlock.getHash().equals(currentBlock.calculateHash()) == false) { //만들어 놓은 Hash값과 계산한 Hash값이 같은지 검증!
System.out.println("Not Equals Current Block Hash!!");
return false;
}

if (currentBlock.getPreviousHash().equals(previousBlock.getHash()) == false) {
System.out.println("Not Equals Previous Block Hash!!");
return false;
}

}

return true;
}


7. 채굴!(마이닝)

 - 작업증명'(Proof-of-Work) : BlockChain의 Block Hash는 `Difficulty`에 따라 선택된  Target 데이터 규격을 만족해야 한다.

  - Block Class 수정( nonce 값 추가)

private int nonce;

/**
* get nonce
*
* @return
*/
public int getNonce() {
return nonce;
}

  - Hash 값 연산하는 부분에 nonce 값 추가

/**
* Hash값 계산하기!
*
* @return
*/
public String calculateHash() {
return BcUtils.generateHash(previousHash, Long.toString(timeStamp), Integer.toString(nonce) , data);
}

  - 채굴 함수 추가

/**
* Mining
*/
public void mineBlock() {
char[] targetChar = new char[BlockChain.DIFFICULTY];
Arrays.fill(targetChar, '0');
String target = String.valueOf(targetChar);

while (hash.substring(0, BlockChain.DIFFICULTY).equals(target) == false) {
nonce++;
hash = calculateHash();
}

System.out.println("Block Mined!!! : " + hash);
}

  - 난이도 추가

public static int DIFFICULTY = 5;

  - 무결성 검증 코드 내 로직 추가

if (currentBlock.getHash().substring(0, DIFFICULTY).equals(hashTarget) == false) {
System.out.println("This block hasn't been mined");
return false;
}

  - 채굴해보기

BLOCK_CHAIN.add(new Block("이건 Genesis Block 입니다.", "0"));
BLOCK_CHAIN.get(0).mineBlock();

BLOCK_CHAIN.add(new Block("이건 두번째 블럭 입니다.", BLOCK_CHAIN.get(BLOCK_CHAIN.size() - 1).getHash()));
BLOCK_CHAIN.get(1).mineBlock();

BLOCK_CHAIN.add(new Block("이건 세번째 블럭 입니다.", BLOCK_CHAIN.get(BLOCK_CHAIN.size() - 1).getHash()));
BLOCK_CHAIN.get(2).mineBlock();

BLOCK_CHAIN.add(new Block("이건 네번째 블럭 입니다.", BLOCK_CHAIN.get(BLOCK_CHAIN.size() - 1).getHash()));
BLOCK_CHAIN.get(3).mineBlock();


 - 결과

Block Mined!!! : 00000fdd8a35c69f5925051899635e1704af98bdd7da3504ed9bc36b5be30901

Block Mined!!! : 00000d8e9a9e735e8f7cbdae41458379ab8b4ac3de7826671eb10f2b7def35bd

Block Mined!!! : 000005dd4dc19710d82dda43d230ec6eabc8041af0fc0a489279802d172f8d41

Block Mined!!! : 000006ff67b1e5735d8891e3e389620f541aa3467e73471a4e2d7b11f841ed3a

=========

hash : 00000fdd8a35c69f5925051899635e1704af98bdd7da3504ed9bc36b5be30901

previousHash : 0

timeStamp : 1532240373740

Data : 이건 Genesis Block 입니다.

nonce : 465518

=========

hash : 00000d8e9a9e735e8f7cbdae41458379ab8b4ac3de7826671eb10f2b7def35bd

previousHash : 00000fdd8a35c69f5925051899635e1704af98bdd7da3504ed9bc36b5be30901

timeStamp : 1532240374917

Data : 이건 두번째 블럭 입니다.

nonce : 1603790

=========

hash : 000005dd4dc19710d82dda43d230ec6eabc8041af0fc0a489279802d172f8d41

previousHash : 00000d8e9a9e735e8f7cbdae41458379ab8b4ac3de7826671eb10f2b7def35bd

timeStamp : 1532240378568

Data : 이건 세번째 블럭 입니다.

nonce : 2130959

=========

hash : 000006ff67b1e5735d8891e3e389620f541aa3467e73471a4e2d7b11f841ed3a

previousHash : 000005dd4dc19710d82dda43d230ec6eabc8041af0fc0a489279802d172f8d41

timeStamp : 1532240383239

Data : 이건 네번째 블럭 입니다.

nonce : 504539

=========

This Blockchain is Valid : true



전체소스

1. Block 소스

package kr.geun.o.bc.basic;

import java.util.Arrays;
import java.util.Date;

/**
* Block Class
*
* @author akageun
*/
public class Block {

private String hash;
private String previousHash;

private String data;
private long timeStamp;

private int nonce;

private Block() {

}

/**
* Block Constructor
*
* @param data
* @param previousHash
*/
public Block(String data, String previousHash) {
this.data = data;
this.previousHash = previousHash;

this.timeStamp = new Date().getTime();
this.hash = calculateHash();
}

/**
* Hash값 계산하기!
*
* @return
*/
public String calculateHash() {
return BcUtils.generateHash(previousHash, Long.toString(timeStamp), Integer.toString(nonce), data);
}

/**
* Mining
*/
public void mineBlock() {
char[] targetChar = new char[BlockChain.DIFFICULTY];
Arrays.fill(targetChar, '0');
String target = String.valueOf(targetChar);

while (hash.substring(0, BlockChain.DIFFICULTY).equals(target) == false) {
nonce++;
hash = calculateHash();
}

System.out.println("Block Mined!!! : " + hash);
}

/**
* get previousHash
*
* @return
*/
public String getPreviousHash() {
return previousHash;
}

/**
* get Hash
* - digital signature
*
* @return
*/
public String getHash() {
return hash;
}

/**
* get TimeStamp
*
* @return
*/
public long getTimeStamp() {
return timeStamp;
}

/**
* get Data
*
* @return
*/
public String getData() {
return data;
}

/**
* get nonce
*
* @return
*/
public int getNonce() {
return nonce;
}
}


2. BcUtils

package kr.geun.o.bc.basic;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* BlockChain Utils
*
* @author akageun
*/
public class BcUtils {

/**
* Hash 생성
*
* @param inputValues
* @return
*/
public static String generateHash(String... inputValues) {
try {
StringBuffer sb = new StringBuffer();
for (String inputValue : inputValues) {
sb.append(inputValue);
}

String input = sb.toString();

MessageDigest digest = MessageDigest.getInstance("SHA-256");

byte[] hash = digest.digest(input.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();

for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}

hexString.append(hex);
}
return hexString.toString();

} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException(e);

} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}


3. BlockChain

package kr.geun.o.bc.basic;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
* BlockChain 메인 클래스
*
* @author akageun
*/
public class BlockChain {

public static List<Block> BLOCK_CHAIN = new LinkedList<>();

public static int DIFFICULTY = 5;

public static void main(String[] args) {

BLOCK_CHAIN.add(new Block("이건 Genesis Block 입니다.", "0"));
BLOCK_CHAIN.get(0).mineBlock();

BLOCK_CHAIN.add(new Block("이건 두번째 블럭 입니다.", BLOCK_CHAIN.get(BLOCK_CHAIN.size() - 1).getHash()));
BLOCK_CHAIN.get(1).mineBlock();

BLOCK_CHAIN.add(new Block("이건 세번째 블럭 입니다.", BLOCK_CHAIN.get(BLOCK_CHAIN.size() - 1).getHash()));
BLOCK_CHAIN.get(2).mineBlock();

BLOCK_CHAIN.add(new Block("이건 네번째 블럭 입니다.", BLOCK_CHAIN.get(BLOCK_CHAIN.size() - 1).getHash()));
BLOCK_CHAIN.get(3).mineBlock();

for (Block block : BLOCK_CHAIN) {
System.out.println("=========");

System.out.println("hash : " + block.getHash());
System.out.println("previousHash : " + block.getPreviousHash());
System.out.println("timeStamp : " + block.getTimeStamp());
System.out.println("Data : " + block.getData());
System.out.println("nonce : " + block.getNonce());
}

System.out.println("=========");

System.out.println("This Blockchain is Valid : " + isValidBlockChain());

}

/**
* 블록 검증
*
*
* @return
*/
private static boolean isValidBlockChain() {
Block currentBlock;
Block previousBlock;

char[] target = new char[BlockChain.DIFFICULTY];
Arrays.fill(target, '0');

String hashTarget = String.valueOf(target);

for (int i = 1; i < BLOCK_CHAIN.size(); i++) { //GenesisBlock 은 제외한다.
currentBlock = BLOCK_CHAIN.get(i);
previousBlock = BLOCK_CHAIN.get(i - 1);

if (currentBlock.getHash().equals(currentBlock.calculateHash()) == false) { //만들어 놓은 Hash값과 계산한 Hash값이 같은지 검증!
System.out.println("Not Equals Current Block Hash!!");
return false;
}

if (currentBlock.getPreviousHash().equals(previousBlock.getHash()) == false) {
System.out.println("Not Equals Previous Block Hash!!");
return false;
}

if (currentBlock.getHash().substring(0, DIFFICULTY).equals(hashTarget) == false) {
System.out.println("This block hasn't been mined");
return false;
}

}

return true;
}
}



참고 페이지

 - https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa

 - http://guruble.com/java-%EC%BD%94%EB%93%9C%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8blockchain/

'BlockChain' 카테고리의 다른 글

블록체인 관련 기본 용어정리.  (0) 2018.02.23