이전 포스팅에 이어서 이번에는 생성된 블록을 체인에 연결하는 코드를 작성해보도록 하겠다.
이전 글)
2022.06.11 - [BlockChain] - BlockChain - TypeScript로 블록체인 만들기 (2) (feat. Jest)
< 목차 >
- 체인 만들기
- 체인 테스트 코드 작성하기
1. 체인 만들기
이번에 만들어 볼 것은 Block 클래스에 의해 생성된 블록들을 체인으로 연결해줄 Chain 클래스이다. Chain 클래스에서는 생성된 블록들을 배열 안에 담아서 말 그대로 blockchain(블록 체인)을 만들고자 한다. 사실상 Block 클래스에 의해 생성된 블록 객체들은 그 안에 있는 속성들에 의해 체이닝이 이미 이뤄지고 있다. 모든 블록 객체들이 previousHash 값을 속성으로 가지고 있기 때문에 특정 블록(A 블록)을 기준으로 이전 블록의 hash 값이 달라질 경우 현재 블록(A 블록)의 previousHash 값과의 불일치가 발생하여 연결고리가 끊기게 된다. Chain 클래스를 따로 만들어서 생성된 블록들을 하나의 배열안에 담아주는 이유는 향후 다루게 될 "난이도"를 계산하기 위함이다. 설명은 이 정도로 하고, 이제 Chain 클래스를 만들어 보면서 서로 연결되어진 블록들을 배열 안에 담아보도록 하자.
테스트 코드를 작성해가면서 작업을 할 예정이기 때문에 위와 같이 chain.ts 파일과 chain.test.ts 파일을 생성해주었다.
그리고 chain.ts 파일 안에서 다음과 같이 Chain 클래스를 만들어주었다.
// chain.ts 파일
import { Block } from "@core/blockchain/block";
export class Chain {
private blockchain: Block[];
constructor() {
this.blockchain = [Block.getGENESIS()];
}
public getChain(): Block[] {
return this.blockchain;
}
public getLength(): number {
return this.blockchain.length;
}
public getLatestBlock(): Block {
return this.blockchain[this.blockchain.length - 1];
}
public addBlock(data: string[]): Failable<Block, string> {
const previousBlock = this.getLatestBlock();
const newBlock = Block.generateBlock(previousBlock, data);
const isValid = Block.isValidNewBlock(newBlock, previousBlock);
if (isValid.isError) return { isError: true, error: isValid.error };
this.blockchain.push(newBlock);
return { isError: false, value: newBlock };
}
}
Chain 클래스의 constructor( ) 함수를 살펴보면 Block 클래스의 getGENESIS( ) 메소드를 이용해 첫번째 블록을 생성한 다음 Block[ ] 타입의 배열 안에 넣어준 것을 확인할 수 있다. 블록 체인을 만들 때 첫번째 블록은 이와 같은 방식으로 배열 안에 담아놓고 시작하게 된다.
Chain 클래스의 경우 "Block 을 요소로 갖는 배열"을 인스턴스로 생성하게 되는데, 해당 인스턴스의 메소드로 블록 체인을 반환하는 getChain( ) 함수 , 체인의 길이를 반환하는 getLength( ) 함수 , 블록 체인 상에서 마지막 블록 (가장 최신 블록)을 반환하는 getLatestBlock( ) 함수를 만들어주었다.
그리고 addBlock( )이라는 메소드를 만들어서 블록 체인 상에 블록이 추가되는 작업을 처리해주었다.
public addBlock(data: string[]): Failable<Block, string> {
const previousBlock = this.getLatestBlock();
const newBlock = Block.generateBlock(previousBlock, data);
const isValid = Block.isValidNewBlock(newBlock, previousBlock);
if (isValid.isError) return { isError: true, error: isValid.error };
this.blockchain.push(newBlock);
return { isError: false, value: newBlock };
}
addBlock( ) 메소드를 살펴보면 우선 getLatestBlock( ) 함수를 통해 블록 체인 상의 마지막 블록을 previousBlock으로 가져오게 된다. 그런 다음 Block 클래스의 메소드인 Block.generateBlock( ) 함수의 인자값으로 previousBlock 과 data를 집어넣어 새로운 블록 (newBlock) 을 생성한다. 새롭게 생성된 블록은 검증 과정을 거쳐야 함으로 Block 클래스에서 미리 만들어 놓은 isValidNewBlock( ) 메소드를 사용해 newBlock의 정보와 previousBlock의 정보를 토대로 검증하는 과정을 거친다.
// Block 클래스의 isValidNewBlock 메소드
public static isValidNewBlock(
_newBlock: Block,
_previousBlock: Block
): Failable<Block, string> {
if (_previousBlock.height + 1 !== _newBlock.height)
return { isError: true, error: "height error" };
if (_previousBlock.hash !== _newBlock.previousHash)
return { isError: true, error: "previousHash error" };
if (Block.createBlockHash(_newBlock) !== _newBlock.hash)
return { isError: true, error: "block hash error" };
return { isError: false, value: _newBlock };
}
검증에 실패하였다면 if 문에 의해 { isError: true, error: isValid.error } 을 return하면서 addBlock( ) 함수가 종료되고 새롭게 생성된 블록은 블록 체인 상에 연결되지 않는다. 검증에 성공하였다면, this.blockchain.push( newBlock ) 에 의해 새로 만들어진 블록이 블록 체인 배열 안에 추가된다.
2. 체인 테스트 코드 작성하기
이제 테스트 코드를 작성하여 Chain 클래스 안에서 만들어준 함수들을 테스트 해보도록 하자.
다음은 chain.test.ts 파일에 작성한 테스트 코드이다.
// chain.test.ts 파일
import { Chain } from "@core/blockchain/chain";
describe("Chain 함수 테스트", () => {
let node: Chain = new Chain(); // [ GENESIS ]
it("getChain() 함수 테스트", () => {
console.log(node.getChain());
});
it("getLength() 함수 테스트", () => {
console.log(node.getLength());
});
it("getLatestBlock() 함수 테스트", () => {
console.log(node.getLatestBlock());
});
it("addBlock 함수 테스트", () => {
for (let i = 1; i <= 5; i++) {
node.addBlock([`Block #${i}`]);
}
console.log(node.getChain());
});
});
터미널에서 명령어 npx jest를 입력해 테스트를 진행해보면,
모든 테스트가 통과된 것을 확인할 수 있다.
지금까지의 내용을 정리해보면 BlockHeader 클래스 , Block 클래스 , Chain 클래스를 사용해 블록을 생성하고 블록 체인 상에 블록을 연결하는 작업을 진행해 보았다. 하지만 블록을 생성하는 과정에 있어서 추가적으로 진행줘야 하는 작업들이 아직 남아있다. "난이도"를 설정해서 블록의 hash 값이 난이도에 의해 형성된 목표값보다 작아질 때까지 연산을 수행하는 PoW (작업 증명 방식)를 구현해야 하며 블록이 생성될 때 코인이 채굴되는 마이닝 (Mining) 부분 역시 구현해야 한다. 해당 부분들에 대한 내용은 다음번 포스팅에서 알아보도록 하겠다.
다음 글)
2022.06.14 - [BlockChain] - BlockChain - TypeScript로 블록체인 만들기 (4) PoW
'BlockChain' 카테고리의 다른 글
BlockChain - 블록체인 P2P 네트워크 만들기 (1) (1) | 2022.06.14 |
---|---|
BlockChain - TypeScript로 블록체인 만들기 (4) PoW (0) | 2022.06.14 |
BlockChain - TypeScript로 블록체인 만들기 (2) (feat. Jest) (1) | 2022.06.11 |
BlockChain - TypeScript로 블록체인 만들기 (1) (0) | 2022.06.11 |
BlockChain - JavaScript로 블록 만들기 (0) | 2022.06.08 |