본문 바로가기
프로그래밍/typescsript

[220608] 블록체인의 구조를 만들어보기

by 한코코 2022. 6. 8.

블록체인의 기본적인 구조는 이렇다.

const block = {
  header:{
    version:'1.0.0',
    pervoiusHash:'',
    hash:'',
    merkleRoot:'',
    difficult:0,
    nonce:0,
  },
  body:{
    data:["ada","sdfasd","ere","eaaaa","zzzz","bbbbb"]
  }
}

 

 

이를 토대로 객체를 만들어보면 다음과 같이 나온다.

const Block {
    constructor(_version,_height,_timestamp,_previousHash,_merkleRoot,_hash,_data){
        this.version = _version;
        this.height = _height;
        this.timestamp = _timestamp;
        this.previousHash = _previousHash;
        this.merkleRoot = _merkleRoot;
        this.hash = _hash;
        this.data = _data;
    }
}

 

 

 

 

문제점

필요한 값이 많기때문에 인자가 엄청나게 길다.

하지만 인자는 3개 이상을 넘지 않도록 하는게 가독성이 좋다.

인자가 3개 이상을 넘을 경우에는 쪼개서 사용하는 경우가 많다.

이게 객체지향(OOP)적 사고 방법인데, 객체를 사용하는 블록체인의 경우는 객체지향적 사고방식이 필수다.

어떻게 사용할지 미리 구조를 설계하고 코드를 정리하며 코드를 작성하지 않는다면 엄청나게 정신사나워진다.

 

 

 

 

코드 개선하기

긴 인자를 정리하기위해 BlockHeader 클래스를 선언했다.

header에 해당하는 값을 BlockHeader에 모두 가져왔다.

Block은 해결되었지만 BlockHeader 인자가 너무 길다.

const BlockHeader {
	constructor(_version,_height,_timestamp,_peviousHash,_merkleRoot,_hash){
        this.version = _version;
        this.height = _height;
        this.timestamp = _timestamp;
        this.previousHash = _previousHash;
        this.merkleRoot = _merkleRoot;
        this.hash = _hash;
    }
}

const Block {
    constructor(_header,_data){
        this.version = _header_version;
        this.height = _header._height;
        this.timestamp = _header._timestamp;
        this.previousHash = _header._previousHash;
        this.merkleRoot = _header._merkleRoot;
        this.hash = _header._hash;
        this.data = _data;
    }
}

const header = new BlockHeader;
const block = new Block(header,data)

 

 

 

 

previousHash와 height

merkleRoot와 hash값은 body값이 있어야 생성할 수 있으므로 객체에서 뺐다.

previousHash는 이전 블록해시값을 저장해야하지만, 지금은 블록을 처음 생성했기때문에 이전값이 없다.

그렇기때문에 맨 처음일 경우는 0을 64개 넣어주었다.

height는 블록이 몇번째로 생성되었는지 자동으로 생성되는 값이라 작성하지 않는다. ????맞냐이거

const BlockHeader {
	constructor(_version,_height,_timestamp,_peviousHash){
        this.version = _version;
        this.height = _height;
        this.timestamp = _timestamp;
        this.previousHash = _previousHash || "0".repeat(64);
    }
}

 

 

 

 

timestamp와 version

1990-01-01부터 현재까지의 일수를 알려주는 timestamp와

현재 사용하고있는 블록버전을 저장하는 version값들은

함수가 생성할때마다 생성되어서 저장되면 메모리낭비다.

원래는 바깥에 다른 함수로 빼서 필요할때만 끌어오는 로직을 만들었겠지만, 이 함수들은 다른 곳에서는 사용하지 않는다.

코드가 길어질수록 다른 곳에 있는 함수 찾기도 일이니까 같이 넣어주었다.

그리고 함수를 바깥에 빼놓은것과 같은 기능을 하도록 static을 붙여주었다.

const BlockHeader {
	constructor(_height,_previousHash){
        this.version = BlockHeader.getVersion();
        this.height = _height;
        this.timestamp = BlockHeader.getTimestamp()
        this.previousHash = _previousHash || "0".repeat(64);
    }
    
    static getVersion(){
    	return "1.0.0"
    }

    static getTimestamp(){
    	return new Date().getTime()
    }
}

 

 

 

 

인스턴스와 static

new라는 키워드를 사용해서 클래스 문법을 사용했을때 나오는 결과물의 객체를 인스턴스라고 함

const header = new BlockHeader(0);
console.log("blockHeader : ",header);


차이점

// static이 없는 메서드
// 에러뜸, 인스턴스를 생성하고나서부터 사용할 수 있는 함수
console.log(header.getVersion());


 // static이 있는 메서드
 // 에러안뜸, 인스턴스 생성하기 전에 사용할 수 있는 함수
console.log(BlockHeader.getVersion());

 

 

 

 

merkleRoot

받은 데이터를 sha256 방식으로 암호화한 해시값을 markleRoot에 넣어준다.

class Block {
    constructor(_header,_data){
        const merkleroot = Block.getMerkleRoot(_data);
        this.merkleRoot = merkleroot;
    }

    static getMerkleRoot(_data){
        const merkleTree = merkle('sha256').sync(data);
        const merkleRoot = merkleTree.root();
        return merkleRoot;
    }
}

 

 

 

 

hash

header와 body값을 받아 최종적인 암호화를 하는 hash

객체 형태인 _header를 string으로 만든 후, _merkleroot 내용과 합친다.

합친 값을 SHA256 방법으로 암호화를 시킨 내용을 문자열로 받아서 반환한다.

class Block {
    constructor(_header,_data){
    	...
        this.hash = Block.createBlockHash(_header,merkleroot);
    }

    static createBlockHash(_header,_merkleroot){
        const values = Object.values(_header);
        const data = values.join('') + _merkleroot;
        return SHA256(data).toString()
    }
}

 

 

 

 

 

 

최종 코드의 모습

const merkle = require('merkle');
class BlockHeader { ... }

class Block {
    constructor(_header,_data){
        const merkleroot = Block.getMerkleRoot(_data);
        this.version = _header._version;
        this.height = _header._height;
        this.timestamp = _header._timestamp;
        this.previousHash = _header._previousHash;
        this.merkleRoot = merkleroot;
        this.hash = Block.createBlockHash(_header,merkleroot);
        this.data = _data;
    }

    static getMerkleRoot(_data){
        const merkleTree = merkle('sha256').sync(data);
        const merkleRoot = merkleTree.root();
        return merkleRoot;
    }

    static createBlockHash(_header,_merkleroot){
        const values = Object.values(_header);
        const data = values.join('') + _merkleroot;
        return data
    }
}

const header = new BlockHeader();
const data = ["I'm checking"];
const block = new Block(header, data);



/*
    결과값
    
    Block {
      version: '1.0.0',
      height: 0,
      timestamp: 1654706457687,
      previousHash: undefined,
      merkleRoot: 'A6D72BAA3DB900B03E70DF880E503E9164013B4D9A470853EDC115776323A098',
      hash: 'c5a0feb89611b60e9b50cffaaab68dc865519445695c7560bad93780ca0a9d97',
      data: [
        'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks'
      ]
    }
*/

댓글