블록체인을 공부하기 위해 계획하던 중 블록체인을 직접 구현해보면 더 자세히 공부할 수 있겠다란 생각에 시작하게 된 가벼운 프로젝트이다.
무작정 시작하기 전 정보 수집을 위해 구글링을 하던 중
https://github.com/Jeiwan/blockchain_go를 발견하게 되었고, 주로 해당 Repo를 참고하며 진행하였다.
구현하고자 하는 것은 블록체인에 기반한 간단한 암호화폐이며, 상세한 코드는
https://github.com/hou27/blockchain_go에서 확인할 수 있다.
구현 순서는 아래와 같다.
Ready to Implement
우선 본격적인 구현에 앞서서 가볍게 블록체인이란 것을 맛보고 넘어가도록 하겠다.
블록체인은 기본적으로 탈중앙화 데이터베이스이다.
최초의 블록부터 시작하여 각각의 블록이 이전 블록과 연결된 linked list이다.
블록체인은 여러 노드에 걸쳐 분산되어 저장 및 관리되며, 블록에는 거래 정보가 포함되어있다.
즉, 모든 사람들이 블록체인의 복제본을 보유하고 있다.
블록체인의 Block이란 간단하게
- 블록의 hash <= hash of (이전 블록의 hash+현재 블록의 data)
- Hash of the previous block
- Block Data
로 이루어져 있다고 할 수 있다.
모든 블록은 이전 블록의 hash와 함께 hash된다.
아무도 block의 내용을 변경할 수 없다.
또한, 반드시 진실된 data만이 추가될 수 있다.
가볍게라도 구현을 해봐야 와닿는다고 생각하기 때문에
우선 '간단한' 블록체인을 Typescript를 이용하여 아래와 같이 구현해보았다.
import * as CryptoJS from "crypto-js";
class Block {
public static calculateHash = (
index: number,
previousHash: string,
timestamp: number,
data: string
): string =>
CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
public static validateStructure = (anyBlock: Block): boolean =>
typeof anyBlock.index === "number" &&
typeof anyBlock.hash === "string" &&
typeof anyBlock.previousHash === "string" &&
typeof anyBlock.timestamp === "number" &&
typeof anyBlock.data === "string";
public index: number;
public hash: string;
public previousHash: string;
public data: string;
public timestamp: number;
constructor(
index: number,
hash: string,
previousHash: string,
timestamp: number,
data: string
) {
this.index = index;
this.hash = hash;
this.previousHash = previousHash;
this.timestamp = timestamp;
this.data = data;
}
}
const genesisBlock: Block = new Block(0, "23092490202", "", 123456, "HiHi");
let blockchain: Block[] = [genesisBlock];
const getBlockchain = (): Block[] => blockchain;
const getNewestBlock = (): Block => blockchain[blockchain.length - 1];
const getNewTimestamp = (): number => Math.round(new Date().getTime() / 1000);
const createNewBlock = (data: string): Block => {
const previousBlock: Block = getNewestBlock();
const nextIndex: number = previousBlock.index + 1;
const nextTimestamp: number = getNewTimestamp();
const nextHash: string = Block.calculateHash(
nextIndex,
previousBlock.hash,
nextTimestamp,
data
);
const newBlock: Block = new Block(
nextIndex,
nextHash,
previousBlock.hash,
nextTimestamp,
data
);
addBlock(newBlock);
return newBlock;
};
const getHashForBlock = (anyBlock: Block): string =>
Block.calculateHash(
anyBlock.index,
anyBlock.previousHash,
anyBlock.timestamp,
anyBlock.data
);
const isNewBlockValid = (candidateBlock: Block, previousBlock: Block): boolean => {
if (!Block.validateStructure(candidateBlock)) {
console.log("This Block isn't valid");
return false;
} 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;
}
};
const addBlock = (candidateBlock: Block): void => {
if (isNewBlockValid(candidateBlock, getNewestBlock())) {
blockchain.push(candidateBlock);
}
};
createNewBlock("second");
createNewBlock("third");
createNewBlock("fourth");
console.log(blockchain);
export {};
https://github.com/hou27/blockchain_ts
class Block {
public static calculateHash = (
index: number,
previousHash: string,
timestamp: number,
data: string
): string =>
CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
public static validateStructure = (anyBlock: Block): boolean =>
typeof anyBlock.index === "number" &&
typeof anyBlock.hash === "string" &&
typeof anyBlock.previousHash === "string" &&
typeof anyBlock.timestamp === "number" &&
typeof anyBlock.data === "string";
public index: number;
public hash: string;
public previousHash: string;
public data: string;
public timestamp: number;
constructor(
index: number,
hash: string,
previousHash: string,
timestamp: number,
data: string
) {
this.index = index;
this.hash = hash;
this.previousHash = previousHash;
this.timestamp = timestamp;
this.data = data;
}
}
우선 Block class를 살펴보겠다.
index : number
hash : string
previousHash : string
data : string
timestamp : number
로 이뤄져있으며,
calculateHash() - 해당 블록의 hash값을 계산한다.
validateStructure() - 블록을 생성하기 위해 입력된 값들이 유효한지 확인한다.
의 메소드를 가지고 있다.
const createNewBlock = (data: string): Block => {
const previousBlock: Block = getNewestBlock();
const nextIndex: number = previousBlock.index + 1;
const nextTimestamp: number = getNewTimestamp();
const nextHash: string = Block.calculateHash(
nextIndex,
previousBlock.hash,
nextTimestamp,
data
);
const newBlock: Block = new Block(
nextIndex,
nextHash,
previousBlock.hash,
nextTimestamp,
data
);
addBlock(newBlock);
return newBlock;
};
이번엔 createBlock을 살펴보겠다.
가장 먼저 이번 블록을 가져온 후, index 값을 지정하고 hash값을 계산한다.
그 다음 준비된 값들을 이용하여 새로운 Block을 생성한 후, addBlock에 넘겨준다.
그렇다면 getNewestBlock를 한번 살펴보도록 하겠다.
const getNewestBlock = (): Block => blockchain[blockchain.length - 1];
DB 등을 이용하고 있지 않으므로 간단하게 이전 블록자체를 리턴하고 있음을 알 수 있다.
const addBlock = (candidateBlock: Block): void => {
if (isNewBlockValid(candidateBlock, getNewestBlock())) {
blockchain.push(candidateBlock);
}
};
다음은 addBlock이다.
createBlock으로부터 넘겨받은 block을 검증하고,
let blockchain: Block[] = [genesisBlock];
이미 선언했던 Block들의 집합으로 이루어진 배열에 push한다.
이 정도만 살펴봐도 최상단에서 확인한 전체 코드들이 쉽게 이해될 것이다.
- 블록의 hash <= hash of (이전 블록의 hash+현재 블록의 data)
- Hash of the previous block
- Block Data
초반에 언급했던 것과 같이 block이 위의 조건을 만족하도록 하여 typescript로 간단하게 구현해보았다.
다음 포스트부터는 Go를 이용하여 간단한 blockchain에서 나아가
Cryptocurrency를 구현해보도록 하겠다.
https://www.investopedia.com/terms/c/cryptocurrency.asp
'Blockchain' 카테고리의 다른 글
Go로 만드는 블록체인 part 5 - Wallet (0) | 2022.01.29 |
---|---|
Go로 만드는 블록체인 part 4 - Transactions (0) | 2022.01.26 |
Go로 만드는 블록체인 part 3 - Persistence (0) | 2022.01.23 |
Go로 만드는 블록체인 part 2 - Proof of Work (0) | 2022.01.10 |
Go로 만드는 블록체인 part 1 - Base of Blockchain (0) | 2021.12.09 |