새 블록 추가, 블록의 연결과 검증
2021. 10. 18. 17:43ㆍBlockchain/Blockchain
코드 작성
전체 코드
const fs = require('fs'); // file system package
const merkle = require('merkle');
const CryptoJs = require('crypto-js');
const SHA256 = require('crypto-js/sha256');
/*
사용법
const tree =
merkle("sha256") // 인자값 : 암호화 방법
.sync([]); // tree 구조로 변환
tree.root(); // root를 가져옴
*/
function getVersion(){
// readFileSync로 내용 가져와서 Json.Parse로 제이슨 형태로 변환 후 비구조할당 처리
const { version } = JSON.parse(fs.readFileSync("../package.json"));
// console.log('패키지 : ', package.toString("utf8"));
// console.log('제이슨 파서 : ', JSON.parse(package).version);
// console.log('version : ', version);
return version;
}
function getCurrentTime(){
// console.log('뉴 데이트 : ', new Date());
// getTime 메서드로 변환 후 ceil()로 소수점 올림 처리
// console.log('겟 타임 : ', Math.ceil(new Date().getTime()/1000));
return Math.ceil(new Date().getTime()/1000);
}
// getVersion();
// getCurrentTime();
// 붕어빵 틀
class BlockHeader{
constructor(version, index, previousHash, time, merkleRoot, difficulty, nonce){ // Header를 만들 인자값 5개
this.version = version; // 1 { version : 1 }
this.index = index; // 2 { version : 1, index : 2}
this.previousHash = previousHash; // 3 { version : 1, index : 2, previousHash : 3}
this.time = time; // ...
this.merkleRoot = merkleRoot; // ......
}
// 안에서 함수 선언도 가능, React에서 했던 Render 등..
// render(){}
}
class Block {
constructor(header, body) {
this.header = header
this.body = body
}
}
function createGenesisBlock() {
// header 만들기 - 5개의 인자값이 필요해! version, index, hash, time, merkle..
const version = getVersion();
const time = getCurrentTime();
const index = 0 // 제네시스 블록은 index=0으로 설정 (최초의 블록이라 우리가 직접 하드코딩)
const previousHash = '0'.repeat(64) // 제네시스 블록은 이전 hash가 없으므로 자리수만 0으로 맞춰서 제공
// body는 배열 형태로
const body = ['hello block']
// body를 가지고 merkletree 값을 구성하기
const tree = merkle('sha256').sync(body)
const root = tree.root() || '0'.repeat(64) // ||사용해서 예외처리
// header 완성시키기
const header = new BlockHeader(version, index, previousHash, time, root)
// console.log(header)
// console.log('asdf', new Block(header, body))
// header 만들어진 결과 가지고 block class에 넣어주기
return new Block(header, body)
}
let Blocks = [createGenesisBlock()];
function getBlocks(){
return Blocks;
}
function getLastBlock(){
return Blocks[Blocks.length - 1];
}
addBlock(['hello2']);
addBlock(["hello3"]);
addBlock(["hello4"]);
// Block push
// addBlock 함수 실행할때 다음 블럭 만들면서, 인덱스값의 변화 필요 +1
function addBlock(data) {
const newBlock = nextBlock(data); // Object Block {header, body}
if (isValidNewBlock(newBlock, getLastBlock())) {
Blocks.push(newBlock);
return true;
// return newBlock;
}
return false;
}
// 새 블록의 유효성 검사
function isValidNewBlock(currentBlock, previousBlock){
// 1. header 검사
// 1-1. currentBlock에 대한 header, body의 Data Type 검사
if (!isValidType(currentBlock)){
console.log(`invalid block ${JSON.stringify(currentBlock)}`);
return false;
}
// 1-2. index 값의 유효성 검사
if (previousBlock.header.index + 1 !== currentBlock.header.index){
console.log(`invalid index`);
return false;
}
// 1.3 previousHash 검사
/*
previousHash previousHash
제네시스 블록 기준 -> 두번째 블록
*/
if (createHash(previousBlock) !== currentBlock.header.previousHash){
console.log(`invalid previousBlock`);
return false;
}
// 2. body 검사
/*
currentBlock.header.merkleRoot -> body [배열]
currentBlock.body -> merkleTree, root -> result !== currentBlock.header.merkleRoot
body의 내용 유무 검사
currentBlock.body.length !== 0 || ( cureentBlock.body로 만든 merkleRoot !== currentBlock.header.merkelRoot )
currentBlock.body.length !== 0 || ( merkle("sha256").sync(currentBlock.body).root() !== currentBlock.header.merkelRoot )
*/
// 2.1 body 내용 유무 검사
if (currentBlock.body.length === 0){
console.log(`invalid body`);
return false;
}
// 2.2 merkleRoot 검사
if (merkle("sha256").sync(currentBlock.body).root() !== currentBlock.header.merkleRoot){
console.log(`invalid merkleRoot`);
return false;
}
return true;
}
// 제네시스 블록 유효성 검사
// 1. 데이터가 바뀐적이 없는지?
// 2. 블록의 모든 배열 확인
function isValidBlock(Blocks){
if (JSON.stringify(Blocks[0]) !== JSON.stringify(createGenesisBlock())){
console.log(`genesis error`);
return false;
}
// Blocks[0] = 이미 위에서 검증이 완료 된 제네시스 블록
let tempBlocks = [Blocks[0]];
// 제네시스 블록을 제외하고 반복
for (let i = 1; i < Blocks.length; i++){
if (isValidNewBlock(Blocks[i], Blocks[i - 1])){
tempBlocks.push(Blocks[i]);
} else{
return false;
}
}
return true;
}
// Type 검사
function isValidType(block){
return(
typeof(block.header.version) === "string" && // string
typeof(block.header.index) === "number" && // number
typeof(block.header.previousHash) === "string" && // string
typeof(block.header.time) === "number" && // number
typeof(block.header.merkleRoot) === "string" && // string
typeof(block.body) === "object" // object
);
}
// 다음 블록의 Header와 Body를 만들어주는 함수
function nextBlock(data){
// header
const prevBlock = getLastBlock();
const version = getVersion();
const index = prevBlock.header.index + 1;
/*
이전 해쉬값에
previousHash = SHA256(version + index + previousHash + timestamp + merkleRoot)
*/
const previousHash = createHash(prevBlock);
const time = getCurrentTime();
const merkleTree = merkle("sha256").sync(data); // 배열
const merkleRoot = merkleTree.root() || '0'.repeat(64);
const newBlockHeader = new BlockHeader(version, index, previousHash, time, merkleRoot);
console.log(new Block(newBlockHeader,data)); // 인자값 data = body
return new Block(newBlockHeader, data);
}
function createHash(block){
const {
version,
index,
previousHash,
time,
merkleRoot,
} = block.header;
const blockString = version + index + previousHash + time + merkleRoot;
const Hash = SHA256(blockString).toString(); // 암호화
return Hash;
}
module.exports = {
// getBlocks,
getLastBlock,
addBlock,
getVersion,
}
'Blockchain > Blockchain' 카테고리의 다른 글
블록체인(Blockchain)의 기본 개념, 제네시스 블록(Genesis Block) 만들기 (0) | 2021.10.18 |
---|---|
블록체인의 특징과 앞으로의 진행 예정 사항 (0) | 2021.09.30 |