Blockchain

#4 block 생성

Sila 2022. 6. 12. 00:23

지금까지 공부한 ts 문법을 이용해 block을 생성해보자.

 

merkleRoot를 만들어주는 라이브러리 merkle은 ts 전용으로도 존재하므로 이를 먼저 설치한다.

 

merkleRoot에 대해서는 https://liferesetbutton.tistory.com/59?category=944752로.

 

npm i merkle
npm i --save-dev @types/merkle
// 둘 모두 설치해야 문제없이 작동한다.

 

1. Block, BlockHeader 셋업

Block class를 우선 선언을 해주되, 전역으로 사용할 수 있도록 @types 폴더에 파일을 만들어 선언한다.

 

/*  @types/Block.d.ts  */

declare interface IBlockHeader {
    version : string
    height : number
    timestamp : number
    previousHash : string
}

declare interface IBlock extends IBlockHeader {
    merkleRoot : string
    hash : string
    data : string[]
}

// extends IBlockHeader 는 IBlockHeader class의 속성을 그대로 상속받는다는 것을 의미한다.

 

이 IBlock, IBlockHeader class는 실제 Block, BlockHeader class를 만드는 템플릿 역할을 해줄 것이다.

 

이제 src/core 폴더로 돌아와 blockchain 폴더를 만들고 blockHeader.ts 파일을 생성한 후,

 

blockHeader부터 만들어 export 해보자.

 

2. BlockHeader 생성

/*  src/core/blockchain/blockHeader.ts  */

export class BlockHeader implements IBlockHeader {
    // class BlockHeader는 먼저 선언해준 IBlockHeader class를 참조한다
    
    public version : string
    public height : number
    public timestamp :number
    public previousHash : string
    // IBlockHeader의 속성값들을 다시 선언해준다. (public/private 붙여서)
    
    constructor ( _previousBlock : IBlock ) {
        this.version = BlockHeader.getVersion()
        this.timestamp = BlockHeader.getTimeStamp()
        // version과 timestamp는 함수를 이용해 지정한다.
        // 함수는 아래에 선언한다.
        
        this.height = _previousBlock.height + 1
        this.previousHash = _previousBlock.hash
    }
    // 전 블럭에서 가져올 데이터가 있기 때문에 그 데이터를 매개변수로서 가져온다.
    // 전 블럭도 블럭이므로 class는 IBlock
    
    public static getVersion() {
        return '1.0.0'
    }
    // static 명령어를 추가함으로써 함수 선언이 함수 호출보다 하단에 있어도
    // 문제 없이 작동할 수 있게 한다.
    // Version을 구하는 함수는 string '1.0.0'을 반환한다.
    
    public static getTimeStamp() {
        return new Date().getTime()
    }
    // timeStamp를 구하는 함수 getTimeStamp는 현재 시간을 timeStamp화해 반환한다. 
}

 

3. Block 생성

마찬가지 방법으로 block.ts 파일을 생성해 여기서 Block class를 생성해 export 한다.

 

/*  src/core/blockchain/block.ts  */

import { BlockHeader } from './blockHeader'

export class Block extneds BlockHeader implements IBlock {
    public hash : string
    public data : string[]
    public merkleRoot : string
    
    constructor(_previousBlock : Block) {
        super(_previousBlock)
        // 매개변수 _previousBlock을 그대로 가져온다.
        
        this.merkleRoot = ''
        this.hash = ''
        this.data = []
        // 이제부터 수정해줄 요소들
    }

 

3.1 data

data는 블럭 생성시 필요한 값으로, 외부에서 가져오는 값이다.

 

이는 constructor의 매개 변수에 추가함으로써 값을 지정할 수 있다. constructor를 다음과 같이 수정한다.

 

/*  src/core/blockchain/block.ts  */

constructor(_previousBlock : Block, _data : string[]) {
    super(_previousBlock)
    
    this.data = _data
}

 

3.2 merkleRoot

다음으로 merkleRoot를 만들어보자. data를 이용해 간단하게 merkleRoot값을 생성해주는 라이브러리를 처음에 설치했다.

 

아래와 같이 사용할 수 있다.

 

/*  src/core/blockchain/block.ts  */

import merkle from 'merkle' // 위에 추가

constructor ( _previousBlock : Block, _data : string[] ) {
    super( _previousBlock)
    
    this.data = _data
    
    const merkleRoot = Block.getMerkleRoot( _data )
    this.merkleRoot
    // ...생략
}

public static getMerkleRoot<T>( _data : T[]) : string {
    const merkleTree = merkle('sha256').sync(_data)
    return merkleTree.root() || '0'.repeat(64)
}
// 다음 함수를 class 안에 추가

 

넣어준 _data 매개변수, merkle함수를 이용해 merleTree를 만든 후, 이를 통해 merkleRoot를 리턴해준다.

 

 

3.3 hash

마지막으로 블럭의 hash값을 만들어보자.

 

/*  src/core/blockchain/block.ts  */

import { BlockHeader } from "./blockHeader";
import merkle from 'merkle'
import { SHA256 } from 'crypto-js'
// crypto-js 라이브러리를 이용해 hash를 만든다.

export class Block extends BlockHeader implements IBlock {
    public hash : string
    public data : string[]
    public merkleRoot : string

    constructor( _previousBlock : Block, _data : string[]) {
        super(_previousBlock)

        this.data = _data

        const merkleRoot = Block.getMerkleRoot(_data)
        this.merkleRoot = merkleRoot
        
        this.hash = Block.createBlockHash(this)
        // createBlockHash 함수를 선언후 호출해 hash값을 생성, 대입힌다.
    }

    public static getMerkleRoot<T>(_data : T[]):string {
        const merkleTree = merkle("sha256").sync(_data)
        return merkleTree.root() || '0'.repeat(64)
    }

    public static createBlockHash(_block :Block) : string {
        const { version, timestamp, height, previousHash, merkleRoot } = _block
        // block의 version, timestamp, height, previousHash,merleRoot 값을 이용해
        // 해당 블럭의 hash 값을 만든다.
        const values = `${version}${timestamp}${height}${previousHash}${merkleRoot}`
        return SHA256(values).toString()
    }
}

 

4. Block 생성 test

여기까지 하면 block의 틀은 전부 만든 것인데, 블럭이 잘 생성되는지 확인하기 위해 테스트를 거쳐야한다.

 

block.test.ts 파일을 만들어 여기 테스트 코드를 짜보자.

 

/*  src/core/blockchain/block.test.ts  */

import { Block } from '@core/blockchain/block'

describe('Block 검증', () => {
    let newBlock : Block
    
    const GENESIS : IBlock = {
        version : '1.0.0',
        height : 0,
        hash : '0'.repeat(64),
        timestamp : 1231006506,
        previousHash : '0'.repeat(64),
        merkleRoot : '0'.repeat(64),
        data : ['Genesis Block']
    }
    // genesis block은 직접 초기값을 지정해주어야 한다.
    
    it('block 생성'), () => {
        const data = ['Blockchain']
        newBlock = new Block ( GENESIS, data )
        
        console.log(newBlock)
    })
})

 

새로운 블럭을 생성할 때는 그 전 블럭의 정보 일부와 새 블럭에 들어갈 데이터들을 매개 변수로 주어야 한다.

 

그런데 첫 번째 블럭 ( genesis block ) 은 그 전 블럭이 존재하지 않으므로

 

이 때만은 우리가 직접 초기값을 정해주어야 한다. (data, previousHash 값 등)

 

위의 GENESIS 변수가 그 첫 번째 블럭 역할을 해줄 것이며, 이를 이용해 새로운 블럭을 생성하는 코드를 만들었다.

 

문제 없이 블럭이 생성되는지 npx jest 를 입력해 테스트 해보자.

 

'Blockchain' 카테고리의 다른 글

#6 chain  (0) 2022.06.12
#5 block 검증  (0) 2022.06.12
#3 typescript - 2  (0) 2022.06.11
#2 typescript  (0) 2022.06.11
#1 Block의 요소  (0) 2022.06.08