노마드 코더의 니꼴라스 강좌를 따라하며 TypeScript를 활용해 간단한 블록체인 구현하기. 나의 강의 목적은 TypeScript 사용 해보는 것.
블록체인의 개념은 깊게 이해하지 못 함.
🌙 프로젝트 설정
- yarn init
- yarn global add typescript
Config 파일 설정
{
"compilerOptions": {
"module": "commonjs",
"target": "ES6",
"sourceMap": true,
"outDir": "dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
"scripts": {
"start": "tsc-watch --onSuccess \" node dist/index.js \" "
}
🤌🏼 코드를 작상하기 전..
- 블록의 Hash 값을 생성하기 위해 "crypto-js" 라이브러리를 설치한다.
crypto-js 라이브러리는 문자들을 결합시켜준다.- 블록체인은 말 그대로 블록들을 체인으로 연결하여 여러개의 블록을 가진 구조를 말하는 것이다. 이 프로젝트에서는 하나의 블록을 Default로 생성 하여 그 블록을 중심으로 여러개의 블록을 생성하여 연결 짓는 것이 목적이다.
index.ts
import * as CryptoJS from 'crypto-js';
class Block {
static calculateBlockHash = (index: number, previousHash:string, data: string, timestamp: number):string => {
/*
CryptoJS 라이브러리는 문자들을 결합해 하나의 문자열로 만든다.
*/
return CryptoJS.SHA256(index + previousHash + data + timestamp).toString();
}
static validateStructure = (aBlock : Block) =>
typeof aBlock.index === "number" &&
typeof aBlock.hash === "string" &&
typeof aBlock.previousHash === "string" &&
typeof aBlock.data === "string" &&
typeof aBlock.timestamp === "number";
public index : number;
public hash : string;
public previousHash : string;
public data : string;
public timestamp : number;
constructor(index: number, hash: string, previousHash: string, data: string, timestamp: number) {
this.index = index;
this.hash = hash;
this.previousHash = previousHash;
this.data = data;
this.timestamp = timestamp;
}
}
/* Default 블록 생성. */
const genesisBlock : Block = new Block(0, "20202020", "", "Hello", 123456);
/* 전체 블록들을 담을 배열. */
let blockchain : Block[] = [genesisBlock];
/* 현재 생성된 블록들을 가져오는 함수 */
const getBlockchain = () :Block[] => blockchain;
/* 현재 블록들 중에 가장 최근에 생성된 블록 가져오는 함수 */
const getLatestBlock = () : Block => blockchain[blockchain.length - 1];
/* 새로운 블록 생성시간 만들기 함수. */
const getNewTimeStamp = () : number => Math.round(new Date().getTime() / 1000);
/*
- 블록을 생성하는 함수.
- Block 클래스의 내장 함수인 calculateBlockHash를 사용하여
새로운 블록을 생성할 수 있게 Param을 전달한다.
- 생성된 Block을 기존 블록들을 담는 List에 Push
*/
const createNewBlock = (data: string): Block => {
const previousBlock: Block = getLatestBlock();
const newIndex: number = previousBlock.index + 1;
const newTimeStamp: number = getNewTimeStamp();
const newHash: string = Block.calculateBlockHash(
newIndex,
previousBlock.hash,
data,
newTimeStamp
);
const newBlock: Block = new Block(
newIndex,
newHash,
previousBlock.hash,
data,
newTimeStamp
);
addBlock(newBlock);
return newBlock;
}
/*
생성된 블록을 받아 해쉬를 생성한다.
*/
const getHashForBlock = (aBlock: Block) : string =>
Block.calculateBlockHash(aBlock.index, aBlock.previousHash, aBlock.data, aBlock.timestamp);
/*
생성된 블록과 최근의 블록 유효성 검사.
*/
const isBlockValid = (candidateBlock : Block, previousBlock : Block) : boolean => {
/* 생성된 블록이 Block 클래스의 타입과 일치하지 않을 경우 */
if(!Block.validateStructure(candidateBlock)) {
return false;
}
/* 최근의 블록의 index와 생성된 블럭의 index + 1 값과 생성된 index 값이 같지 않을 경우 */
else if(previousBlock.index + 1 !== candidateBlock.index) {
return false;
}
/*
최근의 블록의 해쉬값과 생성된 이전의 해쉬값이 일치 하지 않은 경우.
*/
else if(previousBlock.hash !== candidateBlock.previousHash) {
return false;
}
/*
생성된 해쉬 값이 일치하지 않은 경우
*/
else if(getHashForBlock(candidateBlock) !== candidateBlock.hash) {
return false;
} else {
return true;
}
}
/* 유효성 검사 결과가 True일 경우 block 리스트에 추가. */
const addBlock = (candidateBlock: Block) : void => {
if(isBlockValid(candidateBlock, getLatestBlock())) {
blockchain.push(candidateBlock);
}
}
createNewBlock("second block");
createNewBlock("third block");
createNewBlock("fourth block");
console.log(getBlockchain());
export {};
함수 또는 객체, 요소 하나하나의 Type을 지정해주기 때문에 결과의 Type을 미리 예측할 수 있다는 점이 아주 매력적인 TypeScript이다.