코드코코

[블록체인] 블록체인만들기 (1) 블록생성(chainedBlock.js) 본문

블록체인

[블록체인] 블록체인만들기 (1) 블록생성(chainedBlock.js)

코드코코 2021. 12. 30. 00:14

사전작업

chainedBlock.js 파일 생성

touch chainedBlock.js

package.json 파일 생성

npm init

모듈 설치

npm i crypto-js merkle

모듈 불러오기

fs 모듈 사용예정이므로 함께 불러옴

const cryptoJs = require('crypto-js')
const merkle = require('merkle')
const fs = require('fs')

 

블록의 구조

클래스 정의

Block : header와 body로 구성 됨.

class Block {
  constructor(header, body) {
    this.header = header
    this.body = body
  }
}

BlockHeader: 블록의 header에는 6가지 구성요소가 있음. 

- 인덱스는 구분용으로 추가해주었음.

- bit와 nonce에 대해서는 추후 작업증명방식의 합의알고리즘 구성시 변경될 예정

class BlockHeader {
  constructor(index, version, previousHash, timestamp, merkleRoot, bit, nonce) {
    this.index = index
    this.version = version
    this.previousHash = previousHash
    this.timestamp = timestamp
    this.merkleRoot = merkleRoot
    this.bit = bit
    this.nonce = nonce
  }
}

 

제네시스 블럭 생성 과정

제네시스 블럭을 만들 때,제네시스 블럭 생성시 필요한 함수 정의

필요한 정보(index, version, previousHash, timestamp, merkleRoot, bit, nonce)에 대한 함수를 정의

 

getVersion()

-fs.readFileSync : 동기처리(파일을 꼭 읽고 와서 그 후에 실행해야 하는 경우)

function getVersion() {
  const package = fs.readFileSync('package.json')
  //console.log(JSON.parse(package).version)
  return JSON.parse(package).version
}

 

제네시스 블록 생성하기

createGenesisBlock()

최초의 블록을 생성함

BlockHeader클래스로, Block클래스의 header에 넣을, header를 만듦.

new가 붙었으니 객체의 형태로 return 됨.

function createGenesisBlock() {
  const index = 0
  const version = getVersion()
  const previousHash = '0'.repeat(64) // 0을 64번 반복
  const timestamp = parseInt(Date.now() / 1000) // 1000으로 나눈 이유 : 초 단위로 환산하기 위해
  const body = ['Hello block!']
  const tree = merkle('sha256').sync(body)
  const merkleRoot = tree.root() || '0'.repeat(64)
  const bit = 0
  const nonce = 0

  // console.log("version : %s, timestamp : %d, body: %s", version, timestamp, body);
  // console.log("previousHash : %d", previousHash);
  // console.log(tree);
  // console.log("merkleRoot: %s", merkleRoot);

  const header = new BlockHeader(index, version, previousHash, timestamp, merkleRoot, bit, nonce)
  return new Block(header, body)
}

중간 콘솔로그의 결과

테스트용 코드로 확인해보기

const block = createGenesisBlock()
console.log(block);

 

블록체인 구조 구현 과정

 

Blocks 배열 선언 및 할당

Blocks 배열 : 앞으로 생성되는 블럭들이 넣어질 공간

제일 처음으로 제네시스블럭을 Blocks 배열에 담음

let Blocks = [createGenesisBlock()]

Blocks에 들어갈 블록들을 정의할 때 필요한 함수 생성

getBlocks()

전체 블럭을 반환하는 함수.

추후 전체블럭이 유효한지 검증할 때 쓰일 예정

function getBlocks() {
  return Blocks
}

getLastBlock()

마지막 블럭을 반환하는 함수

나의 블록체인과 남의 블록체인을 비교했을 때,

블록이 하나 모자르다면, 모자른 블록을 추가 할때 쓰일 예정

function getLastBlock() {
  return Blocks[Blocks.length - 1]
}

createHash()

해쉬값 생성하는 함수

- 새로운 블럭이 생성될 때 필요

- 함수의 인자로 이전 블록의 값을 받는다.

- 헤더의 모든 값을 스트링 값으로 만들어 모두 더한 것을 SHA256 방식으로 암호화 

function createHash(data) {
  const { index, version, previousHash, timestamp, merkleRoot, bit, nonce } = data.header

  const blockString = index + version + previousHash + timestamp + merkleRoot + bit + nonce

  const hash = cryptoJs.SHA256(blockString).toString()
  return hash
}

테스트용 코드로 확인해보기

//제네시스 블럭생성
const block = createGenesisBlock()

//제네시스 블럭의 해쉬값
const testHash = createHash(block)
console.log(testHash);

Blocks에 들어갈 블록을 정의하는 함수

nextBlock()

- 새로운 블럭을 생성하는 함수

-새로 생성되는 블럭에서 이전 블럭은 현재 가장 마지막 블럭이 됨

-이전 블럭의 해쉬값은 해쉬생성함수에 이전블럭을 넣어 만듦

-함수의 인자값으로 받은 bodydata를 블록의 바디로 반환.

- const header = new BlockHeader(...생략) new BlockHeader는 추후 findBlock함수로 변경될 예정

function nextBlock(bodyData) {
  const prevBlock = getLastBlock()
  const version = getVersion()
  const index = prevBlock.header.index + 1
  const previousHash = createHash(prevBlock)
  const timestamp = parseInt(Date.now() / 1000)
  const tree = merkle('sha256').sync(bodyData)
  const merkleRoot = tree.root() || '0'.repeat(64)
  const bit = 0
  const nonce = 0

  const header = new BlockHeader(index, version, previousHash, timestamp, merkleRoot, bit, nonce)
  return new Block(header, bodyData)
}

테스트용 코드로 확인해보기

const block1 = nextBlock(['transaction1'])
console.log(block1);

Blocks에 블록을 추가하는 함수

addBlocks()

- 바디(데이터)만 받아서 블록에 추가

- 블록을 검증한 후 추가 하는 것이 맞으나, 전반적이 흐름을 확인하기 위한 임시 함수

//바디(데이터)만 받아서 블럭에추가
function addBlock(bodyData) {
  const newBlock = nextBlock(bodyData)
  Blocks.push(newBlock)
}

테스트용 코드로 확인해보기

addBlock(['transaction1'])
addBlock(['transaction2'])
addBlock(['transaction3'])

console.log(Blocks);

모듈로 내보내기

module.exports 사용

외부 파일에서 사용할 수 있도록 모듈화 시킴

검증시 사용할 함수 및 객체를 미리 모듈로 exports 함.

 

module.exports = { nextBlock, getLastBlock, createHash, Blocks }

 

목차

chainedBlock.js
1	사전작업
1.1	chainedBlock.js 파일 생성
-	touch chainedBlock.js
1.2	package.json 파일 생성
-	npm init
1.3	모듈 설치
-	npm i crypto-js merkle
1.4	모듈 불러오기
-	fs 모듈 사용예정이므로 함께 불러옴
2	블록의 구조
3	클래스 정의
3.1	Block : header와 body로 구성 됨.
3.2	BlockHeader: 블록의 header에는 6가지 구성요소가 있음.	
-	인덱스는 구분용으로 추가해주었음
-	bit와 nonce에 대해서는 추후 작업증명방식의 합의알고리즘 구성시 변경될 예정.
4	제네시스 블록 생성 과정
4.1	제네시스 블록 생성시 필요한 함수 정의
-	제네시스 블럭을 만들 때, 필요한 정보(index, version, previousHash, timestamp, merkleRoot, bit, nonce)에 대한 함수를 정의
4.1.1	getVersion()
-	fs.readFileSync : 동기처리(파일을 꼭 읽고 와서 그 후에 실행해야 하는 경우)
4.2	제네시스 블록 생성 함수
4.2.1	createGenesisBlock()
-	최초의 블록을 생성함
-	BlockHeader클래스를 사용하여, Block클래스의 header에 넣을, header를 만듦.
-	new가 붙었으니 객체의 형태로 return 됨.
4.2.1.1	createGenesisBlock() : 콘솔로그 결과 확인
4.2.1.2	테스트용 코드작성 및 확인
4.2.2	createGenesisBlock()를 담을 변수 할당(왜 했지? 단순 콜솔로그인가?)
5	블록체인 구조 구현 과정
5.1	Blocks 배열 선언 및 할당
-	Blocks 배열 : 앞으로 생성되는 블럭들이 넣어질 공간
-	제일 처음으로 제네시스블럭을 Blocks 배열에 담음
5.2	Blocks에 들어갈 블록들을 정의할 때 필요한 함수 생성
5.2.1	getBlocks()
-	전체 블럭을 반환하는 함수.
-	추후 전체블럭이 유효한지 검증할 때 쓰일 예정
5.2.2	getLastBlock()
-	마지막 블럭을 반환하는 함수
-	나의 블록체인과 남의 블록체인을 비교했을 때,
-	블록이 하나 모자르다면, 모자른 블록을 추가 할때 쓰일 예정
5.2.3	createHash()
-	새로운 블럭이 생성될 때 필요
-	함수의 인자로 이전 블록의 값을 받는다.
-	헤더의 모든 값을 스트링 값으로 만들어 모두 더한 것을 SHA256 방식으로 암호화 
5.3	Blocks에 들어갈 블록을 정의하는 함수
5.3.1	nextBlock()
-	새로 생성되는 블럭에서 이전 블럭은 현재 가장 마지막 블럭이 됨
-	이전 블럭의 해쉬값은 해쉬생성함수에 이전블럭을 넣어 만듦
-	함수의 인자값으로 받은 bodydata를 블록의 바디로 반환.
-	const header = new BlockHeader(index, version, previousHash, timestamp, merkleRoot, bit, nonce) 의 new BlockHeader는 추후 findBlock함수로 변경될 예정
5.4	Blocks에 블록 추가하는 함수
5.4.1	addBlock()
-	바디(데이터)만 받아서 블록에 추가
-	블록을 검증한 후 추가 하는 것이 맞으나, 전반적이 흐름을 확인하기 위한 임시 함수
6	모듈로 내보내기
6.1	Module.export 사용
-	외부 파일에서 사용할 수 있도록 모듈화 시킴
-	검증시 사용할 함수 및 객체를 미리 모듈로 exports 함.

전체코드

const fs = require('fs')
const cryptoJs = require('crypto-js')
const merkle = require('merkle')

//블록 구조체 선언 : 헤더, 바디
class Block {
  constructor(header, body) {
    this.header = header
    this.body = body
  }
}

//블록헤더 구조체 선언 : 헤더 구성 요소 나열
class BlockHeader {
  constructor(index, version, previousHash, timestamp, merkleRoot, bit, nonce) {
    this.index = index
    this.version = version
    this.previousHash = previousHash
    this.timestamp = timestamp
    this.merkleRoot = merkleRoot
    this.bit = bit
    this.nonce = nonce
  }
}

function getVersion() {
  const package = fs.readFileSync('package.json')
  //console.log(JSON.parse(package).version)
  return JSON.parse(package).version
}


function createGenesisBlock() {
  const index = 0
  const version = getVersion()
  const previousHash = '0'.repeat(64) // 0을 64번 반복
  const timestamp = parseInt(Date.now() / 1000) // 1000으로 나눈 이유 : 초 단위로 환산하기 위해
  const body = ['Hello block!']
  const tree = merkle('sha256').sync(body)
  const merkleRoot = tree.root() || '0'.repeat(64)
  const bit = 0
  const nonce = 0

  // console.log("version : %s, timestamp : %d, body: %s", version, timestamp, body);
  // console.log("previousHash : %d", previousHash);
  // console.log(tree);
  // console.log("merkleRoot: %s", merkleRoot);

  const header = new BlockHeader(index, version, previousHash, timestamp, merkleRoot, bit, nonce)
  return new Block(header, body)
}

// const block = createGenesisBlock()
// console.log(block);

let Blocks = [createGenesisBlock()]

function getBlocks() {
  return Blocks
}

function getLastBlock() {
  return Blocks[Blocks.length - 1]
}

function createHash(data) {
  const { index, version, previousHash, timestamp, merkleRoot, bit, nonce } = data.header

  const blockString = index + version + previousHash + timestamp + merkleRoot + bit + nonce

  const hash = cryptoJs.SHA256(blockString).toString()
  return hash
}

// //제네시스 블럭생성
// const block = createGenesisBlock()
// //제네시스 블럭의 해쉬값
// const testHash = createHash(block)
// console.log(testHash);

function nextBlock(bodyData) {
  const prevBlock = getLastBlock()
  const version = getVersion()
  const index = prevBlock.header.index + 1
  const previousHash = createHash(prevBlock)
  const timestamp = parseInt(Date.now() / 1000)
  const tree = merkle('sha256').sync(bodyData)
  const merkleRoot = tree.root() || '0'.repeat(64)
  const bit = 0
  const nonce = 0

  const header = new BlockHeader(index, version, previousHash, timestamp, merkleRoot, bit, nonce)
  return new Block(header, bodyData)
}

// const block1 = nextBlock(['transaction1'])
// console.log(block1);

//바디(데이터)만 받아서 블럭에추가
function addBlock(bodyData) {
  const newBlock = nextBlock(bodyData)
  Blocks.push(newBlock)
}


// addBlock(['transaction1'])
// addBlock(['transaction2'])
// addBlock(['transaction3'])

// console.log(Blocks);

module.exports = { nextBlock, getLastBlock, createHash, Blocks }