comnic's Dev&Life

[Blockchain 만들기] 1. Block 만들기 본문

블록체인(Blockchain)

[Blockchain 만들기] 1. Block 만들기

comnic 2019. 4. 28. 08:28
반응형

 블록체인을 이해하기 위한 기본 구현이 간단함에도 불구하고 많이 공유되지 않아 시니어 개발자들도 막연하게 생각하는 경우가 많은 것 같습니다. 더불어 주니어 개발자들은 시도조차 하기 어려워 하는 듯 합니다. 하여, 작년 1월에 교육용으로 사용하면서 올려 놓았던 자료를 다시 정리하여 좀 더 쉽게 정리 해 보고자 합니다.

먼저 개발을 위한 IDE툴은 가장 일반적인 Eclipse를 사용하도록 하겠습니다.

 

1. Block 만들기

블록체인의 가장 기본이 되는 블록의 구조를 먼저 잡아 보도록 하겠습니다. 기본 개념을 잡기 위해 가장 단순한 구조로 먼저 만들고 차츰 발전시켜 나가도록 하겠습니다. 블록체인에서 블록이라하면 기본적으로 이전 블록과 연결성이 있어야 합니다. 자료구조를 배우신 분들은 쉽게 이해하실 수 있을 것입니다. 바로 부모 또는 이전 블록의 주소(여기서는 hash값을 사용합니다.)를 현재 블록이 가지면서 차츰 연장해 나가는 것입니다.

[그림1]

여기서 [#1]이 [#0]을 가리키는 방법이
  1) 각자의 hash를 생성하고 
  2) 그 hash값을 다음 블록이 가지는
것입니다.
hash에 대한 설명은 잠시 뒤에 드리는 것으로 하고, 각각의 해시값이 h0, h1, h2, h3이라고 한다면 아래 그림처럼 각각 이전 주소와 자신의 주소값을 가지도록 하는 것입니다.

[그림2]

그럼 기본적으로 Block의 속성으로 [이전주소, 내주소]를 가질 것입니다.
아래처럼 정의해 보겠습니다.

public class Block {
    public String previousHash;	//이전주소
    public String hash;		//내주소
}

간단합니다. ^^

 

2. Hash 만들기

이제 실제 주소값으로 사용할 hash에 대해서 알아 보겠습니다. 이미 잘 알고 계신 분들은 아래로...
Hash는 다음과 같은 특징을 가집니다.

  • 해싱한 값은 역으로 추측할 수 없습니다.(단방향성)
  • 동일한 입력에 대해 항상 동일한 값을 출력합니다.(검증 가능)
  • 항상 동일한 길이를 가집니다.
  • 입력값이 조금만 달라져도 전혀 다른 값을 출력합니다.

우리는 위와 같은 특징을 가진 Hash 함수로 주로 SHA(Secure Hash Algorithm)을 사용합니다. 주로 SHA256, SHA2, SHA3 이라고 표현하는 것입니다. 우리 예제에서는 통일성만 가지면 어떤 것을 사용해도 괜찮습니다.(이해를 위한 실습이니...)

그럼 java에서는 hash함수를 어떻게 만드는지 살펴보겠습니다.

public String makeHash(String input) {
	try {
		MessageDigest digest = MessageDigest.getInstance("SHA-256");
		byte[] hash = digest.digest(input.getBytes("UTF-8"));
            
		/* byte -> hex string */
		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 (Exception e) {
		throw new RuntimeException(e);
	}
}

조금 복잡해 보일 수 있으나, 사실 대부분의 코드가 byte를 hex string으로 변환하는 과정입니다. 우리가 사용할 hash의 값이 hex string형태라 함수에 함께 추가 하였습니다. 이렇게 함수를 만들어 사용한다고만 이해하시면 될 것 같습니다.
그럼 결과를 살펴보도록 하겠습니다. 정말 위의 특성들을 가지는지.

입력값으로
"hello1"
"hello1"
"hello2"
"hello3"
이라고 각각 입력하고 출력된 값을 살펴보겠습니다.

91e9240f415223982edc345532630710e94a7f52cd5f48f5ee1afc555078f0ab
91e9240f415223982edc345532630710e94a7f52cd5f48f5ee1afc555078f0ab
87298cc2f31fba73181ea2a9e6ef10dce21ed95e98bdac9c4e1504ea16f486e4
47ea70cf08872bdb4afad3432b01d963ac7d165f6b575cd72ef47498f4459a90

먼저 모든 출력값의 길이가 동일하며,
1행과 2행은 동일한 입력값이라 동일한 결과가 나온 것을 알 수 있습니다.
3행과 4행은 입력값이 약간만 달라졌으나 출력값을 완전히 다른 값이 나온 것을 볼 수 있습니다.
(이는 연속된 다량의 데이터를 수집해서 추측하는 것을 방지하기 위한 것입니다.)

 

3. Block Class 만들기

그럼 Block Class를 만들어 보도록 하겠습니다.

위에서 만든 Block class에 생성자와 makeHash를 추가 해 보겠습니다.

package com.comnics.blockchain.simple;

public class Block {
    public String previousHash;	//이전주소
    public String hash;		//내주소
    
    public Block(String previousHash) {
    	this.previousHash = previousHash;
        hash = makeHash(previousHash);
    }
    
    public String makeHash(String input) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(input.getBytes("UTF-8"));

            /* byte -> hex string */
            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 (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

위 코드들을 합친 정도 입니다. 생성자로는 이전 블록의 해시값을 받아서 저장하며, 자신의 주소(해시)를 만들기 위해 인풋으로 전달했습니다.

그럼 간단히 테스트 해 보도록 하겠습니다.

package com.comnics.blockchain.simple;

public class SimpleChain {

    public static void main(String[] arg) {

        Block b0 = new Block("0");
        Block b1 = new Block(b0.hash);
        Block b2 = new Block(b1.hash);
        Block b3 = new Block(b2.hash);

        System.out.println("\n=============================== Block #0 ===============================");
        System.out.println("Prev : " + b0.previousHash);
        System.out.println("Hash : " + b0.hash);

        System.out.println("\n=============================== Block #1 ===============================");
        System.out.println("Prev : " + b1.previousHash);
        System.out.println("Hash : " + b1.hash);

        System.out.println("\n=============================== Block #2 ===============================");
        System.out.println("Prev : " + b2.previousHash);
        System.out.println("Hash : " + b2.hash);

        System.out.println("\n=============================== Block #3 ===============================");
        System.out.println("Prev : " + b3.previousHash);
        System.out.println("Hash : " + b3.hash);

    }
}

쉽게 이해할 수 있도록 풀어서 작성했습니다.

결과는 아래와 같습니다.

=============================== Block #0 ===============================
Prev : 0
Hash : 5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9

=============================== Block #1 ===============================
Prev : 5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9
Hash : 5122a1d1bc9d87662dcf5fb870adf8c55faf2ce12fad1bacac2fe7df88172466

=============================== Block #2 ===============================
Prev : 5122a1d1bc9d87662dcf5fb870adf8c55faf2ce12fad1bacac2fe7df88172466
Hash : 3c857ab8e3936f0f125a97a86262fd3d1cd906f4b7acf1df385f1756f7f692b2

=============================== Block #3 ===============================
Prev : 3c857ab8e3936f0f125a97a86262fd3d1cd906f4b7acf1df385f1756f7f692b2
Hash : 3806743a62ad600a453105090db2884c747600ad27576f4e6ba18f2f56cc445b

살펴보시면 이전 블록의 주소가 자신의 블록 previousHash(출력상 Prev)로 연결되어 있는 것을 볼 수 있습니다.

아래와 같이 코드를 단순화 하여 원하는 만큼의 블록을 생성하고 테스트 해 보시기 바랍니다.

package com.comnics.blockchain.simple;

import java.util.ArrayList;

public class SimpleChain {
	
    public static ArrayList<Block> blockchain = new ArrayList<Block>();

    public static void main(String[] arg) {

        Block genesisBlock = new Block("0");
        blockchain.add(genesisBlock);

        for(int i = 1; i < 10; i++) {
            Block block = new Block(blockchain.get(i - 1).hash);
            blockchain.add(block);
        }

        int blockHeight = 0;
        for(Block block : blockchain) {
            System.out.println("\n=============================== Block #" + blockHeight++ + " ===============================");
            System.out.println("Prev : " + block.previousHash);
            System.out.println("Hash : " + block.hash);
        }

    }
}

간단히 위 코드에 대해 추가적으로 설명을 드리면,
먼저 blockchain이라는 List를 하나 만들고, 최초의 블록인 genesis block을 만들어서 List에 추가한 후 루프를 돌리는 순서입니다.

다음에는 실제 블록에 사용되는 timestamp, nonce, data 필드를 추가하고,
mining(POW의 채굴)에 대해 다루고 구현해 보도록 하겠습니다.

반응형

'블록체인(Blockchain)' 카테고리의 다른 글

[Tendermint]검증자(Validators)  (0) 2021.12.22
[Tendermint]노드 종류  (1) 2021.12.22
[Tendermint]텐더민트 설치 및 실행  (0) 2021.12.22
[Tendermint]텐더민트란?  (1) 2021.11.23
[Tendermint]Tendermint 개요  (0) 2021.11.15
Comments