목표
- 일반블럭 생성 (배열에 넣어주기)
- postman이용하여 확인하기
- 블록검증하기
postman을 활용하여 post방식으로 json형태의 data를 받아왔다. 받아온 data의 body값에 data만을 createBlock() 매개변수로 넣어줬다.
import { getBlocks, createBlock } from './block.js';
app.post('/createblocks', (req, res) => {
res.send(createBlock(req.body.data)); // 요게 이제 우리가 만들 block data
console.log(req.body.data)
})
일반블럭 생성을 위한 함수. 새로운 블럭을 만들어 주기 위해 각각 인자를 새로 정의하고 그에 맞는 값을 넣어주었다. (주석확인하기) 밑에서 작성할 블럭검증을 마친 후 block데이터가 담기는 blocks 배열에 추가해 주었다.
// blockdata를 외부에서 받아온다 (매개변수로)
const createBlock = (blockData) => {
const previousBlock = blocks[blocks.length - 1]; // 맨마지막 block불러오기
const nextIndex = previousBlock.index + 1; // 맨마지막블럭 index값의 +1
const nextTimestamp = new Date().getTime() / 1000 // 현재시간을 가져와서 초단위로 나눠주기
const nextHash = calculateHash(nextIndex, blockData, nextTimestamp, previousBlock.hash) // 앞에서 가져온 값들 이용하기
const newBlock = new Block(nextIndex, blockData, nextTimestamp, nextHash, previousBlock.hash);
if (isValidNewBlock(newBlock, previousBlock)) {
blocks.push(newBlock);
return newBlock
}
console.log('fail to create new block')
return null;
export { getBlocks, createBlock }
}
새로 생성된 블럭과 이전 블럭을 비교하여 검증하는 코드를 구현했다. 검증은 다음과 같은 3단계로 구성하고, 블럭의 구조는 각각의 데이터타입 비교를 통해 해보도록 하자. 가독성을 위해 isValidBlockStructure() 함수로 빼서 작성했다.
// 길어서 함수로 빼기
const isValidBlockStructure = (newBlock) => {
if (typeof(newBlock.index) === 'number'
&& typeof(newBlock.data) === 'string'
&& typeof(newBlock.timestamp) === 'number'
&& typeof(newBlock.hash) === 'string'
&& typeof(newBlock.previousHash) === 'string'
) {
return true;
}
return false;
}
const isValidNewBlock = (newBlock, previousBlock) => {
if (newBlock.index !== previousBlock.index + 1 ) {
console.log('invalid index')
return false;
}
else if (newBlock.previousHash !== previousBlock.hash) {
console.log('invalid previous hash')
return false;
}
else if (isValidBlockStructure(newBlock) == false) {
console.log('invalid block structure')
return false;
}
return true;
}
결과 값
새로운 블럭 생성 후 index번호, post를 활용한 data내용, previousHash값 등 잘 출력된것을 확인할 수 있다.
block.js
// 블록체인 관련 함수
// 블록 구조 설계
/*
index : 블록체인의 높이
data : 블록에 포함된 모든 데이터 (트랜잭션 포함
timestamp : 블록이 생성된 시간
hash : 블록 내부 데이터로 생성한 sha256 값 (블록의 유일성)
previousHash : 이전 블록의 해쉬 (이전 블록을 참조)
*/
import CryptoJS from 'crypto-js'
// class로 블록 만들기
class Block {
constructor(index, data, timestamp, hash, previousHash){
this.index = index; // height
this.data = data;
this.timestamp = timestamp;
this.hash = hash;
this.previousHash = previousHash;
}
}
// 위에 block안의 외부에서 주어지는 index값들을 합해서 sha256 으로 계산? 변환? / 이걸 쓰려면 CryptoJS 모듈을 쓰면된다
const calculateHash = (index, data, timestamp, previousHash) => {
return CryptoJS.SHA256((index + data + timestamp + previousHash).toString()).toString();
//return CryptoJS.SHA256((2).toString()).toString();
}
// 이 block이 유일무이 하다는것을 증명해주는거지 16지수 64자리
let testHash = calculateHash(11, 20, 50, 1560);
//console.log(testHash)
// genesis block 만들기
const createGenesisBlock = () => {
const genesisBlock = new Block (0, 'genesis block!!', new Date().getTime() / 1000, 0, 0);
genesisBlock.hash = calculateHash(
genesisBlock.index,
genesisBlock.data,
genesisBlock.timestamp,
genesisBlock.previousHash
)
return genesisBlock
}
// 저장해줄 자료구조를 만들기
// genesisblock을 선언할때 한번만 배열에 값으로 들어가도록. 첫번째 인덱스
const blocks = [createGenesisBlock()];
// 외부에 노출할 수 있게 보여주기
function getBlocks() {
return blocks;
}
// blockdata를 외부에서 받아온다 (매개변수로)
const createBlock = (blockData) => {
const previousBlock = blocks[blocks.length - 1]; // 맨마지막 block불러오기
const nextIndex = previousBlock.index + 1; // 맨마지막블럭 index값의 +1
const nextTimestamp = new Date().getTime() / 1000 // 현재시간을 가져와서 초단위로 나눠주기
const nextHash = calculateHash(nextIndex, blockData, nextTimestamp, previousBlock.hash) // 앞에서 가져온 값들 이용하기
const newBlock = new Block(nextIndex, blockData, nextTimestamp, nextHash, previousBlock.hash);
if (isValidNewBlock(newBlock, previousBlock)) {
blocks.push(newBlock);
return newBlock
}
console.log('fail to create new block')
return null;
}
// 블록의 무결성 검증
/*
- 블록의 인덱스가 이전 블록인덱스보다 1 크다.
- 블록의 previousHash가 이전 블록의 hash이다.
- 블록의 구조가 일치해야 한다.
*/
// 길어서 함수로 빼기
const isValidBlockStructure = (newBlock) => {
if (typeof(newBlock.index) === 'number'
&& typeof(newBlock.data) === 'string'
&& typeof(newBlock.timestamp) === 'number'
&& typeof(newBlock.hash) === 'string'
&& typeof(newBlock.previousHash) === 'string'
) {
return true;
}
return false;
}
const isValidNewBlock = (newBlock, previousBlock) => {
if (newBlock.index !== previousBlock.index + 1 ) {
console.log('invalid index')
return false;
}
else if (newBlock.previousHash !== previousBlock.hash) {
console.log('invalid previous hash')
return false;
}
else if (isValidBlockStructure(newBlock) == false) {
console.log('invalid block structure')
return false;
}
return true;
}
export { getBlocks, createBlock }
httpServer.js
// web에 명령어를 입력해서 내 노드를 제어하는 서버
// import의 경우 딱 필요한 애만 불러옴
import express from 'express';
import bodyParser from 'body-parser';
import { getBlocks, createBlock } from './block.js';
// common js에서 통쨰로 다 불러옴 그래서 위 import가 더 빠름
//const express = require('express')
// 초기화 함수
const initHttpServer = (myHttpPort) => {
const app = express();
app.use(bodyParser.json());
app.get('/', (req, res) => {
res.send('Hello BlockChain!')
})
app.get('/blocks', (req, res) => {
// 블록의 배열을 넘겨주는 함수를 여기서 갖다 쓰자 (block.js)
res.send(getBlocks());
})
app.post('/createblocks', (req, res) => {
// 여기서 뭐 어찌저찌 postman에서 쏴주는거 해야겠지?
res.send(createBlock(req.body.data)); // 요게 이제 우리가 만들 block data
console.log(req.body.data)
})
//매개변수 2개 / 함수포트 , 화살표함수
app.listen(myHttpPort, () => {
console.log('listening httpServer Port : ', myHttpPort);
})
}
// main에서 import를 하려면 export를 먼저하자
export { initHttpServer };