채굴을 위해 사용되는 mining 함수는 이전 블록의 해시값, 시간, 거래내역, 난이도(diff)를 인자로 받는다. 그리고 해당 값과 난스, 해시값을 합쳐 블록(block)을 만든다. 블록은 반복적인 해시값 계산을 통해 난이도를 통과하면 최종적으로 반환된다.
이렇게 난스를 증가시켜가며 난이도에 맞는 해시값을 발견하는게 바로 '채굴(마이닝)'이다.
따라서, 마이닝을 위해선 difficulty를 알아야하는데, 이를 위해서 일단 chain을 생성해주어야한다.
chain은 쉽게 말하면 배열이다.
difficulty는 지속적으로 변하는데(난이도조정), 기준이 이전 블록이므로, 연결은 반드시 이루어져 있어야한다.
코드를 작성하기 전에,
라이브러리를 설치해주는 것을 잊지말자!
merkle, crypto-js, hex-to-binary세 개 install 해주기. typescript 같은 경우, typescript 버전도 받아주어야함.
src/core/config.ts
/**
* 난이도 조정 블록 범위
*/
export const DIFFICULTY_ADJUSTMENT_INTERVAL: number = 10
/**
* 블럭 생성 시간 (단위 : 분)
*/
export const BLOCK_GENERATION_INTERVAL: number = 10
/**
* 블럭한개당 생성되는 시간
*/
export const BLOCK_GENERATION_TIME: number = 60
export const GENESIS: IBlock = {
version: '1.0.0',
height: 0,
hash: '0'.repeat(64),
timestamp: 1231006506,
previousHash: '0'.repeat(64),
merkleRoot: '0'.repeat(64),
difficulty: 0,
nonce: 0,
data: ['GENESIS BLOCK'],
}
src/core/blockchain/chain.ts
import { Block } from '@core/blockchain/block'
import { DIFFICULTY_ADJUSTMENT_INTERVAL } from '@core/config'
export class Chain {
public blockchain: Block[]
constructor() {
this.blockchain = [Block.getGENESIS()]
}
public getChian(): Block[] {
return this.blockchain
}
public getLength(): number {
return this.blockchain.length
}
public getLatestBlock(): Block {
return this.blockchain[this.blockchain.length - 1]
}
public addBlock(data: string[]): Failable<Block, string> {
//할것: 내가 앞으로 생성할 블록의 높이값 가져올수 있음?
// 현재높이값, - block interval=음수값
//난도 구해야함
//생성시간을 구하는 것
const previousBlock = this.getLatestBlock()
const adjustmentBlock: Block = this.getAdjustmentBlock() // -10 Block 구함
const newBlock = Block.generateBlock(previousBlock, data, adjustmentBlock)
const isVaild = Block.isValidNewBlock(newBlock, previousBlock)
if (isVaild.isError) return { isError: true, error: isVaild.error }
return { isError: false, value: newBlock }
}
/**
* 생성기준으로 블럭높이가 -10 짜리 구해오기.
*/
public getAdjustmentBlock() {
// 현재 마지막블럭에서 - 10 (DIFFICULTY_ADJUSTMENT_INTERVAL)
const currentLength = this.getLength()
const adjustmentBlock: Block =
currentLength < DIFFICULTY_ADJUSTMENT_INTERVAL
? Block.getGENESIS()
: this.blockchain[currentLength - DIFFICULTY_ADJUSTMENT_INTERVAL]
return adjustmentBlock
}
}
src/core/blockchain/block.ts
import { SHA256 } from 'crypto-js'
import merkle from 'merkle'
import { BlockHeader } from './blockHeader'
import { BLOCK_GENERATION_INTERVAL, DIFFICULTY_ADJUSTMENT_INTERVAL, GENESIS } from '@core/config'
import hexToBinary from 'hex-to-binary'
/**
* 객체를 만들기 위함.
* constructor() 객체 만들려고
* this = {
* version : 'asdfasf'
* hash : 'asdfasdfasdfasf',
* merkleRoot.:'asdfasdf',
* data : ['asdf','asdfasdf','asfdasdf']
* }
*/
export class Block extends BlockHeader implements IBlock {
public hash: string
public merkleRoot: string
public nonce: number
public difficulty: number
public data: string[]
constructor(_previousBlock: Block, _data: string[], _adjustmentBlock: Block = _previousBlock) {
super(_previousBlock)
const merkleRoot = Block.getMerkleRoot(_data)
this.merkleRoot = merkleRoot
this.hash = Block.createBlockHash(this)
this.nonce = 0
this.difficulty = Block.getDifficulty(this, _adjustmentBlock, _previousBlock)
this.data = _data
}
public static getGENESIS(): Block {
return GENESIS
}
public static getMerkleRoot<T>(_data: T[]): string {
const merkleTree = merkle('sha256').sync(_data)
return merkleTree.root() || '0'.repeat(64)
}
public static createBlockHash(_block: Block): string {
const { version, timestamp, merkleRoot, previousHash, height, difficulty, nonce } = _block
const values: string = `${version}${timestamp}${merkleRoot}${previousHash}${height}${difficulty}${nonce}`
return SHA256(values).toString()
}
public static generateBlock(_previousBlock: Block, _data: string[], _adjustmentBlock: Block): Block {
const generateBlock = new Block(_previousBlock, _data, _adjustmentBlock)
// TODO : newBlock은 마이닝이 완료된 블럭
const newBlock = Block.findBlock(generateBlock)
return generateBlock
}
//여기부터 마이닝
public static findBlock(_generateBlock: Block): Block {
// TODO : 마이닝 작업 코드를 넣어야함.
// _generateBLock Block이 담김
// hash
// _generateBlock.hash // -> 2진수
// hexToBinary(_generateBlock.hash)
let hash: string
let nonce: number = 0
while (true) {
nonce++
_generateBlock.nonce = nonce
hash = Block.createBlockHash(_generateBlock)
// 16 -> 2
const binary: string = hexToBinary(hash)
const result: boolean = binary.startsWith('0'.repeat(_generateBlock.difficulty))
if (result) {
_generateBlock.hash = hash
return _generateBlock
}
}
}
//난이도 작업
public static getDifficulty(_newBlock: Block, _adjustmentBlock: Block, _previousBlock: Block): number {
console.log(_newBlock, _adjustmentBlock)
if (_newBlock.height < 9) return 0
if (_newBlock.height < 19) return 1
if (_adjustmentBlock.height === 0) return 0
//시간을 구할 때 열 번쨰 배수의 블록일 때만 검사하기를 원함.
if (_newBlock.height % DIFFICULTY_ADJUSTMENT_INTERVAL !== 0) return _previousBlock.difficulty
// ~여기가 실행~
const timeTaken: number = _newBlock.timestamp - _adjustmentBlock.timestamp //결과값은 초로 나옴. ex 2000초
const timeExpected: number = 60 * BLOCK_GENERATION_INTERVAL * DIFFICULTY_ADJUSTMENT_INTERVAL //6000
if (timeTaken < timeExpected / 2) return _adjustmentBlock.difficulty + 1
// 예상시간보다 너무 빨리 만들어졌는데? 난이도 +1
else if (timeTaken > timeExpected * 2) return _adjustmentBlock.difficulty - 1
return _adjustmentBlock.difficulty
// 예상시간보다 너무 느리게 만들어지는데? 난이도 -1
}
//유효성 검증
static isValidNewBlock(_newBlock: Block, _previousBlock: Block): Failable<Block, string> {
if (_previousBlock.height + 1 !== _newBlock.height) return { isError: true, error: '블록높이가 맞지않습니다.' }
if (_previousBlock.hash !== _newBlock.previousHash)
return { isError: true, error: '이전해시값이 맞지 않습니다.' }
if (Block.createBlockHash(_newBlock) !== _newBlock.hash)
return { isError: true, error: '블록해시가 올바르지 않습니다.' }
return { isError: false, value: _newBlock }
}
}
findBlock 함수가 마이닝이다.
원리는 아래와 같다.
블록에서 hash와, data를 제외한 나머지 값들로 hash값을 생성한다.
해당 hash를 2진수로 변환한다.
2진수로 변환된 숫자가 몇개의 0으로 시작하는지 확인한다.
0의 갯수가 difficulty 보다 큰지 확인한다.
difficulty 보다 크면 통과!
difficulty 보다 작으면 nonce 값을 1 증가 시킨다.
그리고 이걸 반복한다.
Reference :
https://kong-dev.tistory.com/166
https://hajoeun.blog/block-chain-js-1
'블록체인' 카테고리의 다른 글
[Typescript, 블록체인] P2P 네트워크를 통한 브로드캐스팅 구현하기 (0) | 2022.06.15 |
---|---|
[Typescript] 타입스크립트로 블록체인 P2P 구현해보기(찍먹) (0) | 2022.06.14 |
블록체인 P2P 기술에 대해 알아보자. (0) | 2022.06.14 |
[Typescript] 타입스크립트로 블록체인 블록 검증하기 (0) | 2022.06.14 |
[Typescript] 타입스크립트로 블록체인 블록 구현하기 (0) | 2022.06.09 |