지금까지 공부한 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 |