brunch

You can make anything
by writing

C.S.Lewis

by 에디의 기술블로그 Oct 09. 2018

왕초보자를 위한 Java로 이해하는 블록체인(1)

- 블록체인 및 비트코인 알고리즘 공부, 자바 코드로 구현해보기

이 글은 필자가 블록체인에 대해서 잘 모르는 상황에서, 스터디를 위해 준비했던 글입니다. 블록체인은 그동안 많은 변화가 있었기에 해당 글을 읽는 것을 추천하지 않습니다.. 






최근에 회사 업무로 블록체인 서비스를 담당하게 되었다. 회사 업무는 블록체인 관련 기술을 활용하는 플랫폼은 아니고,  외부 API를 사용하여 가상화폐 정보를 사용자에게 보여주는 웹서비스이다. 데이터를 서빙하는 웹서비스로, 블록체인 기술은 몰라도 상관없는 상황이다. 비록 블록체인 기술을 몰라도 상관없지만, 최근 블록체인에 대한 대중의 관심이 높아지는 상황이고 필자 역시 서비스를 맡게 되면서 아주 기초적인 상식은 필요하다고 판단하여 블록체인에 대해서 간략하게 공부를 시작하였다.


반나절 공부하였고, 급하게 공부하였기 때문에 내용이 매우 허접합니다. 해당 글을 통해서 좋은 정보를 전달하는 목적도 있지만 초보자의 시선에서 작성한 기초적인 내용이라서 다른 사람들에게 얼마나 도움이 될지는 모르겠습니다. 혹시라도 읽고 계시는 블록체인 전문가 분들께서는 잘못된 내용에 대해서 피드백 부탁드립니다. 



목차

What is a Hash?

블록체인이란?

Java로 구현해보는 블록체인



What is a HASH?

Hash란?

Hash(해시)란 프로세스에서 객체를 구별하기 위한 고유한 값인데, Hash Function 에 의해서 반환된다. Java에서의 대표적인 Hash Function 은 hashCode() 메서드가 있다. 


hashCode()

Java에서 Hash값을 출력해주는 메서드가 바로 hashCode()이다. Heap 메모리의 Reference Address를 16진수로 출력한다. String 클래스의 hashCode() 메서드는 아래와 같다. 

하지만, 이 글에서는 String 클래스의 hashCode() 메서드를 사용하지 않는다. 필자는 SHA-256 암호화가 적용된 Hash 값을 활용할 것이다. 


SHA-256

SHA-256 Hash값은 아래 코드와 같이 조회할 수 있다. 참고로, SHA256 암호화를 사용하기 위해서 MessageDigest 클래스를 import 해야 한다. 


public class StringUtil {

생략...

 

public static String getSha256(String str){

        String SHA;

        try{

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

            sh.update(str.getBytes());

            byte byteData[] = sh.digest();

            StringBuffer sb = new StringBuffer();

            for (byte aByteData : byteData) {

                sb.append(Integer.toString((aByteData & 0xff) + 0x100, 16).substring(1));

            }

            SHA = sb.toString();

        }catch(NoSuchAlgorithmException e){

            e.printStackTrace();

            SHA = null;

        }

        return SHA;

    }


메서드를 테스트해보자. 아래와 같이 임시 문자열의 SHA-256 해시 값을 구해보자. 

d0718e5d2ff9ed4338e9b2e438ff1c85d8df58fdd3836720097aa0cb1a3d0a1f


5ceedc48e1204f6602f733d9901e1732a90f319afac1168115a8f3d802877596


Hash 값이 출력되었다. 첫 번째는 "블록체인_데이터_1"에 대한 Hash이고, 두 번째는 "블록체인_데이터_2"에 대한 Hash이다. 만약 같은 문자열에 대한 Hash 출력은 어떻게 되는가? 같은 문자열은 같은 Hash 값을 출력한다. 


같은 문자열이라면 같은 Hash 값을 출력한다. 또한 문자열마다 유일한(Unique)한 값을 생성한다. 


원본 문자열이 같으면 같은 Hash 나온다.

일부라도 다르면 전혀 다른 Hash 가 나온다.

Hash 값을 통해 원본 문자열을 유추할 수 없다.

Hash 값이 같으면 원본 문자열이 같음을 신뢰할 수 있다. 


정리

Java에서 SHA-256 암호화 방식으로 Hash 값을 출력하였다. 근데, SHA-256 Hash 값이 블록체인이랑 무슨 상관이 있는가??  이제 슬슬 블록체인에 대해서 알아보자. 




블록체인이란?

블록체인 세계에는 수많은 코인이 존재하고, 계속 신규 코인이 개발이 되고 있다. 각 코인은 어떤 알고리즘을 사용하고 있나? 필자는 블록체인 전문가는 아니라서, 자세히는 모른다. 이 글은 블록체인 기술 중 가장 대표적인 1세대 기술이 적용된 비트코인 알고리즘에 대해서 검토한다.


블록체인

블록체인은 "블록으로 이어진 체인"을 뜻한다. 그림에서 하나의 실버 고리가 하나의 블록이 된다. 일반적으로 체인은 중간에 하나의 고리만 깨져도 줄이 끊어지게 된다. 블록체인 역시 하나의 블록이 깨져버리게 된다면 블록체인이 끊어지는 구조이다. 자 그럼, 블록체인에서 말하는 블록이란 도대체 무엇인가? 블록에는 A와 B 사이에 거래한 데이터가 기록되는 기록지(?)이다. 기록지는 일반적으로는 "장부"라는 표현을 많이 사용한다. 거래한 데이터, 즉 장부에는 다양한 정보가 포함될 수 있다. 예를 들어서, 얼마의 돈을 거래했는지, 언제 거래했는지, 누구랑 거래했는지 등 포함된다. 해당 데이터를 보통 "트랜잭션"이라고 부른다. 필자는 이 글에서는 트랜잭션이라고 표현하지 않고 "거래 정보"라는 표현을 사용하겠다. 


일부 용어에 대한 설명이 잘못되었다면 피드백 부탁드립니다. 필자는 블록체인 공부한 지 며칠 안된 초보입니다.

블록에는 각각의 Hash 값을 가지고 있어야 한다. 일반적으로 블록의 Hash 값은 유일하면서 유니크한 값을 가지게 된다. 또한 블록은 이전 블록에 대한 Hash 값도 알고 있어야 한다. 왜냐하면, 블록으로 이어진 체인을 만들기 위해서는 이전 블록에 대한 Hash 값을 알아야 하기 때문이다. 만약, 이전 블록에 대한 Hash 값을 분실한다면 체인은 끊어질 것이다. 아래 그림을 참고하자.

[출처] https://spectrum.ieee.org/computing/networks/how-blockchains-work  


간단하게 정리하면 블록에는 아래와 같은 요소로 구성된다.

이전 블록의 Hash 값

거래 정보(데이터)

Nonce  (Nonce에 대한 설명은 나중에 하겠다)

해당 블록의 Hash 값


글 초반에 예시로 나온 데이터를 블록체인으로 구성하면 어떻게 될까? 블록체인_데이터_1과 블록체인_데이터_2 의 Hash 값은 아래와 같다. 


d0718e5d2ff9ed4338e9b2e438ff1c85d8df58fdd3836720097aa0cb1a3d0a1f


5ceedc48e1204f6602f733d9901e1732a90f319afac1168115a8f3d802877596


해당 블록 데이터는 아래와 같이 체인으로 구성된다. 

필자의 그림이 참 허접하지만 매우 심플하다. 각 블록은 이전 블록 Hash 값을 가지고 있다. 또한 각각의 블록은 각자의 거래정보와 Hash 값을 가진다. 제일 처음의 블록은 이전 블록이 없기 때문에 Null 값이 들어가 있다. 사실, 실제 블록체인은 훨씬 복잡할 것이다. 여기서 중요한 사실은 아래와 같다.


Hash 값은 정해진 규칙에 의해서만 생성이 된다. 

예를 들어서 "Hash 데이터는 앞 문자열이 반드시 '00000' 이 포함되어야 한다"라는 규칙이 있다면 데이터는 아래와 같이 생성된 경우에만 유효할 것이다.


000000f1c85d8df58fdd383672...... 


또는 다른 예로, 뒤에 문자열이 반드시 '00000' 이 포함되어야 한다 라는 규칙이 있다면 아래와 같이 생성된 경우만 유효할 것이다. 


d0718e5d2ff9ed4338e........000000


Hash 값은 이런 식으로 정해진 규칙에 의해서 생성이 되는 것이다. 일단, 대충 개념만 잡고 넘어가자..... ㅠㅠ


블록 Hash는 어떻게 생성되는가? 

자... 그럼, 블록 Hash는 어떻게 생성되는가? 블록 Hash는 이전 Hash 데이터와 거래정보를 합쳐서 만든 문자열에 Hash Function을 사용하여 Hash 값을 구한다. 근데, 필자는 두 개의 요소를 더 추가하겠다. 


현재 시간에 대한 타임스탬프

1씩 증가하는 값 (Nonce이다...)


어떤 시간 + 어떤 거래 정보 + 이전 Hash 값을 조합하여 문자열을 만들 수 있고, 해당 문자열에 Hash Function을 사용하면  Hash 값을 구할 수 있다. 이 글 초반에 설명했듯이 문자열이 동일하다면, 생성되는 Hash 값 역시 동일한 Hash 값이 생성될 것이다. 


다른 Hash 값을 나오게 하고 싶다면 어떻게 하면 될까? 

정답은!! 바로 문자열을 계속 바꿔주면 되는 것이다. 이때 사용하는 것이 바로 "1씩 증가해서 더하는 값"이다. 이 글을 "Nonce"라고 부른다. 


어떤 시간 + 어떤 거래 정보 + 이전 Hash 값 + Nonce(1)   ---> 다른 Hash 값이 나옴

어떤 시간 + 어떤 거래 정보 + 이전 Hash 값 + Nonce(2)   ---> 역시 또 다른 Hash 값이 나옴 

어떤 시간 + 어떤 거래 정보 + 이전 Hash 값 + Nonce(3)   ---> 역시 또 다른 Hash 값이 나옴

.... 내가 원하는 규칙이 나올 때까지 계속 for 문 실행 


이런 식으로 1을 계속 증가해서 문자열에 추가하면서 특정 규칙에 맞는 Hash값이 나올 때까지 삽질(무한 반복)을 하는 것이다. 정말 무식한 방법처럼 보이지만, 지금 설명한 이 방식이 블록체인 1세대 기술이며, 비트코인의 알고리즘이다. 해당 알고리즘이 proof of work 알고리즘이다.


proof of work algorithm

비트코인의 알고리즘인, proof of work algorithm 알고리즘은 기본적으로 SHA-256를 기반으로 한다. 자세한 내용은 아래 링크를 참고하자. 

https://en.bitcoin.it/wiki/Proof_of_work


기존 거래와 무엇이 다른가?

간단하게 다시 정리를 해보겠다.  주저리주저리 TMI....

거래 정보를 포함하고 있는 각각의 블록은 체인으로 이어져서 블록체인을 구성한다. 체인으로 이어지기 위해서 이전 블록의 Hash 값을 알고 있어야 하고, 각자 블록은 "이전 블록 Hash + 거래정보 + Nonce + 타임스탬프"로 구성된 문자열을 만들어, 유니크한 Hash 값을 가지고 있다. 해당 Hash는 정해진 규칙에 맞는 Hash값의 형태를 가져야 한다. 유일하고, 유니크한 Hash 값은 역으로 해킹해서 어떤 거래정보 인지 알아내기가 쉽지가 않다. 또한 Hash 값이 변조가 되면 체인이 끊어지는데, 변조가 되는 순간에 변조 여부를 바로 알아차릴 수가 있다. 왜냐면 체인이 끊어졌다는 것을 서로 연결되어있는 블록이 알기 때문이다. 자 간단하게 정리했다. 


기존 거래와 무엇이 다른가???  

기존 거래 정보는 중앙 집중식으로 한 곳에 저장되지만, 블록체인 거래 정보는 각자 개인 모두가 저장하며 공유한다. 필자가 재미있게 본 미생이라는 웹툰이 있다. 원인터내셔널이라는 회사에 장그래, 오과장, 장백기 라는 영업사원이 있다. 각각 1건씩 영업 거래 실적이 있는데 장그래, 오과장 은 거래처A 와 거래를 하였고, 장백기는 거래처B와 거래를 하였다. 기업은행이라는 은행을 통해서 거래를 하였다. 은행은 중앙집중식 거래의 대표적인 예이다. 거래 정보, 즉 거래한 장부는 은행에 모두 저장되어있다. 그럴 일이 없겠지만 만약 은행이 해킹당하면 거래 정보는 모두 사라질 수 있다. 물론 은행에 저장된 거래정보 외로 따로 각자 개인이 거래한 정보를 따로 기록하였다면 각자 개인의 거래 정보는 가지고 있을 수는 있다. 하지만 은행에 거래 정보가 없는 상황에서 개인이 작성한 거래 정보를 100% 믿을 수 있는가?

기존 중앙집중식 거래


블록체인 세계에서 아래 그림처럼 이론상으로는 아래와 같다.  실제로는 현실은 다르다.


블록체인 세계에서의 거래

각각의 거래는 체인처럼 이어져 있다. 거래1+거래2+거래3 으로 이어져 있는 거래정보리스트는 각자 개인이 모두 갖고 있고 서로 공유한다. 거래처에서도 모두 갖고 있는 것이다. 장그래가 거래처A와 거래한 거래1 정보는 오과장, 장백기, 심지어는 다른 거래처인 거래처B 에서도 알수 있다. 투명하게 서로 거래 정보를 알 수 있는 것이다. 만약에 오과장이 거래2에 대한 정보를 위조했다면 거래 2에 대한 Hash 값이 변경될 것이고, 거래 3에서 갖고 있는 이전 Hash 값과 일치하지 않게 된다. 블록체인이 끊어진 것이다. 그렇다면 이때 블록체인이 끊어졌다는 사실을 누가 알 수 있나? 


체인이 끊어졌다는 사실을 모두 알 수 있게 된다. 왜냐면 거래 정보를 모두 공유하고 있기 때문이다.

하지만 블록체인 세계의 현실에서는 어떤가?  현실적으로 모든 거래 정보를 각자 개인이 저장해서 알고 있을 수가 없다. 거래 정보가 수천만 건이라고 가정하면 그 정보를 장그래 PC 1대로 저장할 수 있을까??? 현실적으로는 불가능한 것이다. 그래서 존재하는 것이 바로 거래소가 필요한 것이다. 거래 정보를 대신 갖고 있어야 하는 누군가가 또 필요한 것이었다. 근데 만약 거래소가 해킹당하면? 사실 이미 이런 사건이 몇 번 발생하였다. 최악의 경우에는 일부 거래 정보가 없어질 수도 있다. 기존 중앙 집중식 거래 방식과는 전혀 다른 방식으로, 각자 거래 정보를 갖고 공유한다는 이론적으로는 매우 훌륭한 알고리즘이었지만, 이론을 모두 현실에 반영하기에는 한계가 있는 기술이었다. 


이 글에서는 블록체인 기술에 대해서 좋고 나쁨을 논하지는 않습니다. 또한 블록체인 기술 중 1세대 기술인 비트코인에 대해서만 공부합니다.


정리

간단하게, 블록체인이 무엇인지 정리하였다. 1세대 비트코인을 넘어서 2세대,3세대,4세대 로 넘어가면서 지속적으로 블록체인은 발전을 하고 있다.  

대충 블록체인에 대해서 이해가 되셨나요? 이해가 되었고, 글이 재미가 있다면 아래 이어지는 글을 읽어도 좋습니다. 만약 느낌이 안 오고, 글이 드럽게 지루하다면 이 글은 이제 안 보셔도 됩니다. 필자의 설명이 너무 엉망이라서 그런 것입니다. 저도 사실 블록체인 잘 몰라요


자바로 블록체인 만들기

주저리주저리 재미없는 얘기는 그만하고, 본격적으로 자바로 블록체인을 만들어보자. 참고로 필자가 스프링 공부 중이라서 스프링부트 환경에서 개발하였다. 굳이 스프링 환경에서 할 필요는 전혀 없다. 이 글에서 스프링 기술은 전혀 중요하지 않다. 


Block 클래스

Block 클래스에는 해당 블록의 Hash, 이전 블록의 Hash, 거래 정보 데이터 등의 필드로 구성된다.


public class Block {

    @Getter

    private String hash;

    @Getter

    private String previousHash;

    @Getter

    private String data; //Transaction

    private long timeStamp;

    private int nonce;

    private String target = "00000";

    private int targetDepth = 5;

...


Block 클래스 생성자에는 아래와 같이 코인을 채굴하는 minNewBlock 메서드를 호출한다.


 public Block(String data,String previousHash ) {

        this.data = makeHashData(data);

        this.previousHash = previousHash;

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

        mineNewBlock();

    }


     /**

     * 신규 블록체인을 생성한다.

     */

    private void mineNewBlock(){

        // 조건에 맞는 Hash 값을 찾을 때까지 계속 반복한다.

        while(hash == null || !hash.substring(0, targetDepth).equals(target)) {

            nonce ++;

            hash = makeHashBlock();

        }

    }



    /**

     * Hash 값을 조회한다.

     */

    public String makeHashBlock() {

        return StringUtil.getSha256(

                    previousHash +

                    Long.toString(timeStamp) +

                    data +

                    Integer.toString(nonce)

        );

    }


코인을 채굴하는 부분(삽질을 통해서 정해진 규칙에 맞을 때까지 무한반복)을 하는 로직은 mineNewBlock 메서드에서 수행한다. 여기서 필자가 정한 규칙은 아래와 같다. 


Hash 값의 제일 앞부분이 00000 으로 시작하는지 맞춰본다. (target='00000')


Hash값은 nonce가 1 씩 증가함에 따라서 계속 변경되면서 무한 실행한다. 만약 계속 실행되는 과정에서 Hash 값의 앞부분이 00000 으로 시작하는 순간이 오면, 해당 반복문을 빠져나온다. 이 상황이 채굴에 성공한 상황이라고 볼 수 있다. 물론 실제 비트코인의 규칙이 필자가 정한 규칙과 같지는 않다. (참고로 비트코인은 10분에 1코인이 생성될 수 있도록 규칙이 계속 변경된다고 알고 있다.) 어쨌든 이렇게 삽질(?)을 통해서, 컴퓨팅 작업을 수행하는 알고리즘이 바로 proof of work이다. 


Block List (체인) 

Block을 체인처럼 연결해보자. 필자는 ArrayList 자료구조를 사용하겠다. 필자는 Respository 레이어에 정의하였다.


@Repository

public class BlockRepository {

    private static ArrayList<Block> blockChain = new ArrayList<>();

    public ArrayList<Block> findAllBlockChain() {

        return blockChain;

    }

}


그다음에 Service 레이어에서 BlockRepository 를 사용하는 로직을 구현하였다. 참고로 필자는 Provider 레이어에 작성하였고 어노테이션은 @Component 로 정의하였다.


@Component

@RequiredArgsConstructor

public class BlockProvider {

    private final BlockRepository blockRepository;

    

   public ArrayList<Block> findAllBlockChain(){

        return blockRepository.findAllBlockChain();

    }

    

    public void mineBlock(String data){

        String previousHash = findAllBlockChain().isEmpty()? "0" : findAllBlockChain().get(findAllBlockChain().size() - 1).getHash();

        findAllBlockChain().add(new Block(data, previousHash));

    }

생략...


mineBlock 메서드가 신규 Block를 생성하는 로직이다. findAllBlockChain().add 하면서 신규 Block 객체를 넣어주는데, 이때 실행할 때 중요한 것은 Block 객체를 생성하면서 이전 Block 의 Hash 값을 매개변수로 넘겨줘야 한다. 만약 이전 Hash 가 없다면, 즉 제일 처음 생성한 Block 이라면 이전 Block가 없으므로 "0"을 넘겨준다.  new Block(data, previousHash) 생성자를 통해서 신규 코인이 채굴되는 것이다. 테스트를 위해서 간단한 컨트롤러를 작성하였다. 필자처럼 대충 작성하지 말고 테스트 코드를 작성하는 게 현명하다. 


@RestController

@RequiredArgsConstructor

@RequestMapping("/blocks")

public class BlockController {

    private final BlockProvider blockProvider;

    @GetMapping

    public Collection<Block> findAllBlockChain(){

        if(!blockProvider.isChainValid()){

            //블록체인 깨진 경우

        }

        return blockProvider.findAllBlockChain();

    }

    @PostMapping

    public String mineBlock(@RequestParam(name="data") String data){

        blockProvider.mineBlock(data);

        return "OK";

    }

}


아주 간단하게 블록체인을 자바 코드로 구현하였다. 

https://github.com/sieunkr/blockchain-sample/tree/master/spring-blockchain



정리

이번 글에서는 블록체인 1세대 기술에 대해서 빠르게 공부하고, Java로 간단하게 블록체인을 구현하였다. 필자는 아주 심플하게 구현했지만, 실제 블록체인 기술은 훨씬 복잡할 것이다. 이번 글은 블록체인을 생성하는 로직까지만 구현하였는데 추후에 시간이 된다면 블록체인 거래가 어떻게 발생하는지 공부하고 Java로 구현할 예정이다. 



브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari