블록체인은 관리 대상 데이터를 '블록'이라고 하는 소규모 데이터들이 P2P 방식을 기반으로 생성된 체인 형태의 연결고리 기반 분산 데이터 저장 환경에 저장하여 누구라도 임의로 수정할 수 없고 누구나 변경의 결과를 열람할 수 있는 분산 컴퓨팅 기술 기반의 원장 관리 기술이다
참고 : 블록체인 - 위키백과
필요한 파일 목록
block.js
- blockchain 관련 function 선언
- block 구조 설계
httpServer.js
- 웹에 명령어를 입력해서 내 노드를 제어하는 서버
p2pServer.js
- 다른 노드와 통신을 위한 서버
main.js
- p2p 서버 초기화
- http 서버 초기화
- blockchain 함수 사용
express 모듈을 통해 httpserver를 만들어주는 function 작성
변수로 port를 받아 localhost:port에서 서버를 연다
const initHttpServer = (myHttpPort) => {
const app = express();
app.use(express.json()); // express에 bodyparser 내장됨
app.use(express.urlencoded({extended : true}));
app.get("/", (req, res) => {
res.send("Hello world!")
})
app.listen(myHttpPort, ()=> {
console.log("listening httpServer... Port : ",myHttpPort)
})
}
export { initHttpServer };
웹소켓을 이용해 다른 노드와 통신 할 것이다
웹소켓 서버 열어주는 function 작성
변수로 port번호를 받아 웹소켓 서버를 연다
import WebSocket from "ws";
import { WebSocketServer } from "ws";
const sockets = []; //배열 시작점의 주소를 sockets에 저장
const initP2PServer = (p2pPort) => {
const server = new WebSocketServer({port:p2pPort});
server.on("connection", (ws) => {
initConnection(ws);
})
console.log("listening P2PServer Port : ", p2pPort)
}
const initConnection = (ws) => {
sockets.push(ws);
}
export { initP2PServer };
각각 서버 열어주는 function을 불러와 서버 초기화를 해준다
import {initHttpServer} from "./httpServer.js";
import { initP2PServer } from "./p2pServer.js";
const httpPort = parseInt(process.env.HTTP_PORT) || 3001;
const p2pPort = parseInt(process.env.HTTP_PORT) || 6001;
// 직접 숫자를 집어넣는 것은 프로그래머만 vscode를 통해수정 할 수 있다
// 하지만 .env파일을 참조하게 해놓으면 외부에서 수정할 수 있음
initHttpServer(httpPort);
initP2PServer(p2pPort);
class를 이용해 block의 기본 구조를 만든다
class Block {
constructor(index, data, timestamp, hash, previousHash) {
this.index = index;
this.data = data;
this.timestamp = timestamp;
this.hash = hash;
this.previousHash = previousHash;
}
}
만들어진 block들을 가지고 있는 배열 blocks가 있다
이를 외부(예를 들면 httpServer.js에서 blocks를 쓰려고 할 때)에 노출할 수 있는 함수 getBlocks()를 만든다
const getBlocks = () => { // 외부에 노출할 수 있게
return blocks;
}
block구조에서 hash는 직접 계산해야 하기 때문에 이를 계산하는 함수를 만든다
import CryptoJS from "crypto-js"
const calculateHash = (index, data, timestamp, previousHash) => {
return CryptoJS.SHA256(`${index + data + timestamp + previousHash}`).toString();
// toString() 한 것이 우리가 원하는 형태
}
첫번째 genesisblock을 만드는 함수를 짠다
const createGenesisBlock = () => {
const genesisBlock = new Block(0, 'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks',new Date().getTime() / 1000, 0, 0);
genesisBlock.hash = calculateHash(genesisBlock.index, genesisBlock.data,
genesisBlock.timestamp, genesisBlock.previousHash);
return genesisBlock;
}
블록의 무결성을 검증하는 function을 만든다
const isValidBlockStructure = (newBlock) => {
return(
typeof (newBlock.index) === "number"
&& typeof (newBlock.data) === "string"
&& typeof (newBlock.timestamp) === "number"
&& typeof (newBlock.hash) === "string"
&& typeof (newBlock.previousHash) === "string"
)
}
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)) {
console.log("invalid block structure");
return false;
}
return true;
}
블록의 무결성을 확인하고 다음 블락을 만드는 코드를 작성한다
// blockdata는 변수로 받는다
const createBlock = (blockData) => {
const previousBlock = blocks[blocks.length - 1];
const nextIndex = previousBlock.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) // blocks 배열에 해당 block을 추가한다
return newBlock;
}
console.log("fail to create newblock");
return null;
}
blocks 배열을 확인하고 새로운 block을 추가하는 코드를 작성한다
// httpServer.js에서 추가
app.get("/blocks", (req, res) => {
res.send(getBlocks());
})
app.post("/createBlock",(req, res) => {
res.send(createBlock(req.body.data))
})
node main.js로 서버를 실행시키고
localhost:3001/blocks에 접속하면 제네시스 블록이 들어가있는 blocks 배열을 확인할 수 있다
현재는 post를 할 수 있는 방법이 따로 없으므로 postman에서 post를 테스트해본다
body.data에 string을 담아 post를 해보면 새로 생긴 newBlock을 확인할 수 있다
다시 localhost:3001/blocks에 접속해보면 newBlock이 추가된 blocks 배열을 확인 할 수 있다