그냥 사는 이야기

간단한 블록체인을 구현해보며 이해하기 본문

Development/기타

간단한 블록체인을 구현해보며 이해하기

없다캐라 2020. 4. 29. 18:33
반응형

블록체인이라고 하면 비트코인이나 이더리움을 떠올릴 수 있겠지만, 이것은 암호화폐(Cryptocurrency)로 분류할 수 있습니다. 암호화폐를 이루는 기술들은 다양합니다. 블록체인은 그 기술 중 일부인 셈입니다.

그렇게 분리해서 생각한다면 블록체인만 따로 떼어내서 이해해 볼 수도 있으며 이를 작은 모형부터 직접 구현해 보면 그 실체를 훨씬 이해하기 쉽다고 생각합니다. 초보개발자를 위해 쉬운 개념부터 접근해 보겠습니다.

블록과 체인

블록과 체인을 하나씩 코드로 구현한다면 무엇으로 표현 할 수 있을까요?

  • 블록 - 관리 대상 데이터
  • 체인 - 데이터간 연결

처음 이 개념을 접했을 때 블록은 구조체(혹은 객체)로, 체인은 링크드 리스트 같은 참조를 떠올렸습니다. 그렇다면 링크드 리스트(흔히 Singly list) 같은 형태가 떠올랐습니다.

단순한 링크드 리스트 모습

블록체인도 단순하게 그림으로 표현한다면 사실 거의 흡사합니다. 다만 포인터 참조가 아닌 해시값 참조로 연결되어 있으며 해시를 이해해야 합니다.

블록해시

여기서의 해시는 해시함수를 의미하며 위키 정의를 살펴보면,

해시함수란 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수이다.

라고 정의되어 있습니다. 해시함수는 아래와 같은 특성이 있습니다.

  1. 어떠한 값을 입력값으로 주어도 비슷한 길이의 알 수 없는 난수가 결과로 출력된다.
  2. 입력 데이터가 한 글자만 바뀌어도 완전히 다른 결과가 출력된다.
  3. 출력 값으로 입력값을 예측할 수 없다.
  4. 같은 내용으로 입력값을 주면 결과값은 항상 같다.

블록에는 어떤 데이터를 담고 있을 것이며 이것을 해싱하면 고정된 길이의 해시값이 출력이 될 것입니다.

블록 구현

블록은 구조체로 표현될 수 있습니다. 그렇다면 어떤 변수들을 담으면 될까요? 현재는 이해를 돕기 위해 가장 단순한 모델을 가정해보겠습니다.

  1. 이전 블록의 해시값을 기억할 변수
  2. 현재 블록에서 간직해야 할 데이터

체인 구현

블록을 생성하였다면 직전에 만든 블록과 관계를 맺어줘야 합니다. 블록체인에서는 이전의 블록 해시값을 그다음 블록이 기억을 해주고 이런 과정을 반복적으로 해주는 관계를 블록체인이라고 합니다.

단 하나, 예외적인 경우라면 최초의 블록입니다. 이때는 이전의 블록이 존재하지 않기에 해시값을 없거나 무의미한 랜덤 값을 넣어줍니다. 이후의 생성될 블록체인 생성과정속에 씨앗을 만드는 것으로 볼 수 있으며 이런 최초의 블록은 Genesis 블록이라고 부릅니다.

작업 증명(Proof of Work)

블록체인과 링크드 리스트와 다른 점은 크게는 연결관계에 있습니다. 총 3개의 블록이 생성된 블록체인을 가정해보겠습니다.

3개의 블록체인

2번째 블록의 해시값은 6BQ1이며 다음번 블록에서 기억하고 있습니다. 하지만 2번째 블록에서 누군가가 수정을 가한다면 3번째 블록에서 기억하고 있는 이전 해시값은 변경이 필요해집니다. 그렇다면 3번 블록의 해시값 자체도 변경이 되기에 이후에 블록이 있다면 모두 한 땀 한 땀 수정이 이루어져야 합니다. 바로 이런 점이 링크드 리스트와 다른 개념입니다.

하지만 사람이 수정하는 것도 아니고 컴퓨터가 해줄 건데 그게 뭐가 대수냐 할 수도 있습니다. 그렇다면 컴퓨터에게도 조금은 시간이 걸리게끔 작업을 주도록 해보겠습니다.

Nonce

컴퓨터에게 작업을 준다? 그래서 시간을 지연시키게끔 한다? 시간 지연은 어려운 일이 아니지만 지연 자체도 의미가 있게끔 해야 합니다. 그래서 보통 사용하는 방법은 해시값을 어차피 구할 거면 앞에 "0"으로 시작하는 해시값을 구하도록 유도하는 것입니다. 하지만 아무리 해시값이 랜덤처럼 보인다 해도 0으로 시작한다는 보장은 없습니다. 이럴 때 입력값을 조금씩 변경시키면서 0으로 시작할 때까지 루프를 돌면서 해시값을 구하도록 합니다. 이렇게 조금씩 변경시키는 차이? 값을 Nonce라고 합니다.

결국 이렇게 nonce를 변경하면서 최종 해시를 구하는 것을 작업 증명이라고 합니다. 일종의 숙제 결과물 같은 값입니다. 이 값이 적절한지는 해시값을 구할 때만큼 오래 걸리지 않습니다. nonce값을  넣고 바로 해시값을 구해서 일치하면 증명이 바로 되는 것이지요. 다만 이 값을 구할 때까지는 nonce를 얼마큼 변경해야 할지 알 수 없기에 한 땀 한 땀 루프 속에서 찾아야만 합니다.

결론

블록체인이 곧 암호화폐는 아닙니다. 저는 현재까지 블록체인을 활용하여 구현한 것 중 가장 성공모델이 암호화폐라고 생각합니다. 비트코인과 이더리움은 투기성을 떠나 아직까지는 생존하는 프로젝트이기 때문입니다. 암호화폐가 아닌 다른 경우에서는 계속하여 아이디어를 실현해보려고 도전하고 있는 단계입니다.

블록체인은 이 외에도 뒷받침 되어야 할 기술들이 상당히 많습니다. 그래서 처음 이 기술에 진입하시려는 분들에게는 좋은 진입점이 되기 위해 가장 단순한 뼈대 기술을 구현해보면서 타 기술들의 필요성을 이해한다면 충분히 빠르게 이해할 수 있으리라 생각합니다.

위의 내용을 구현한 간단한 코드는 파이썬과 자바로 작성해보았습니다. 참고하시면 되겠습니다.

class Block():
    def __init__(self, data, prevhash=''):
        self.data = data
        self.prevhash = prevhash
        self.nonce = 0
        self.hash = self.calc_hash()

    def calc_hash(self):
        block_string = (self.prevhash + str(self.data) + str(self.nonce)).encode()
        return hashlib.sha256(block_string).hexdigest()

    def mine_block(self):
        while self.hash[:5] != str('').zfill(5):
            self.nonce += 1
            self.hash = self.calc_hash()

    def __str__(self):
        string = "nonce: " + str(self.nonce) + '\n'
        string += "data: " + str(self.data) + '\n'
        string += "prevhash: " + str(self.prevhash) + '\n'
        string += "hash: " + str(self.hash)

        return string


class BlockChain():
    def __init__(self):
        genesis_block = self.generate_genesis_block()
        self.chain = [genesis_block, ]
        print(genesis_block)
        print()

    @staticmethod
    def generate_genesis_block():
        return Block('Genesis Block')

    def get_last_block(self):
        return self.chain[-1]

    def add_block(self, new_block):
        new_block.prevhash = self.get_last_block().hash
        new_block.mine_block()
        self.chain.append(new_block)
        print(new_block)
        print()


if __name__ == "__main__":
    simple_blockchain = BlockChain()

    simple_blockchain.add_block(Block('2nd'))
    simple_blockchain.add_block(Block('3rd'))
class Block {
    private String previousHash;
    private String data;
    private int nonce;

    private String hash;

    static public MessageDigest messageDigest;

    static {
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }

    public Block(String previousHash, String data) {
        this.previousHash = previousHash;
        this.data = data;
        nonce = 0;

        calculateHash();
    }
    // 일부 get/set 함수들 ...

    public String getHash() {
        return hash.toLowerCase();
    }

    public void setPreviousHash(String previousHash) {
        this.previousHash = previousHash;
    }

    public String toBlock() {
        return getPreviousHash() + getData() + getNonce();
    }

    public String toString() {
        return "nonce : " + getNonce() + '\n' +
                "data : " + getData() + '\n' +
                "previousHash : " + getPreviousHash() + '\n' +
                "hash : " + getHash();
    }

    public void calculateHash() {
        final byte[] bytes = messageDigest.digest(toBlock().getBytes());
        setHash(byteArrayToHexString(bytes));
    }

    public void mineBlock() {
        while (!getHash().startsWith("00000")) {
            setNonce(getNonce() + 1);
            calculateHash();
        }
    }

    public static String byteArrayToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();

        for (byte b : bytes) {
            sb.append(String.format("%02X", b & 0xff));
        }
        return sb.toString();
    }
}

class BlockChain {
    private List<Block> blocks = new ArrayList<>();

    public BlockChain() {
        blocks.add(new Block("", "Genesis Block"));
        System.out.println(blocks.get(0) + "\n");
    }

    public void addBlock(Block block) {
        block.setPreviousHash(blocks.get(blocks.size() - 1).getHash());
        block.mineBlock();
        blocks.add(block);
        System.out.println(block + "\n");
    }
}

public class SimpleBlockChain {

    public static void main(String[] args) {
        BlockChain blockChain = new BlockChain();

        blockChain.addBlock(new Block("", "2nd"));
        blockChain.addBlock(new Block("", "3rd"));
    }
}

'Development > 기타' 카테고리의 다른 글

Hyperledger Fabric 거래처리 방식  (2) 2020.05.08
Hyperledger Fabric 살펴보기  (0) 2020.05.06
Kibana - Search, Aggregation  (0) 2020.01.25
ElasticSearch - Search, Aggregation  (0) 2020.01.25
ElasticSearch - index_document_CRUD_mapping  (0) 2020.01.25
Comments