[Blockchain] JS로 Blockchain 구현하기 (3)

Jaewonee·2022년 4월 28일

목표

  • 일반블럭 생성 (배열에 넣어주기)
  • 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() 함수로 빼서 작성했다.

  • 블록의 인덱스가 이전 블록인덱스보다 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;
}

최종코드

결과 값
새로운 블럭 생성 후 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 };
profile
🙋‍♂️블록체인 개발자 되기 / 📑 공부기록 공간

0개의 댓글