즐코

논스 추가 + 난이도 조절 및 블록 체인 만들기 본문

BlockChain

논스 추가 + 난이도 조절 및 블록 체인 만들기

YJLEE_KR 2022. 6. 14. 18:43

저번 포스팅에 이어서 블럭에 논스를 추가하고 블럭들 간의 난이도를 조절하는 과정을 추가한다. 그리고 이 업데이트한 블록을 같은 배열안에 넣어 블록 체인 형태를 만들어보고자 한다.

 

1. 난이도 조절을 위한 설정값 만들기 

 

난이도라는 건 목표값보다 낮은 해시를 찾기 위한 논스값을 연산하는 데 얼마나 오래 걸리냐이다.

즉, 새로운 블록 생성 시간이 난이도와 연관되어 있다. 늦게 생성되면 난이도가 어렵다는 것이고, 빨리 생성되면 난이도가 낮다는 것이다. 

(관련 개념 포스팅 : https://yjleekr.tistory.com/75?category=1284408) 

 

 그렇기때문에 난이도의 평균시세(?)를 구하기 위해서 2가지 상수가 필요하다.

1/ 이상적으로 생각하는 블록의 평균 생성 시간 

2/ 난이도 측정에 필요한 블록 개수, 간격 단위

 

위 2가지 상수 및 분을 초로 바꾸기 위한 단위도 config.ts 상에 넣어준다.

// src/core/config.ts

// 난이도 조정 블록 범위
export const DIFFICULTY_ADJUSTMENT_INTERVAL: number = 10;

// 블록 생성 시간 (단위 : 분) 10*60
export const BLOCK_GENERATION_INTERVAL: number = 10;

// 분->초로 바꾸기 위한 단위
export const UNIT: number = 60;

 

 

2. Chain 클래스 생성

블록은 개별적으로 존재하는 것이 아니라 체이닝되어있다. 여기선 배열에다가 블록을 추가하는 방식으로 블록 체인을 구현한다. 

그렇다면, Chain 클래스는 Block을 가지고 있는 배열이므로 데이터 타입이 Block[]이 될 것이다.

 

애초에 첫 생성 블럭인 GENESIS 블럭을 배열에 넣고 시작한다.

각 메서드를 설명하면 다음과 같다.

 

- getChain() : 블록체인 자체를 반환

- getLength() : 블록체인의 길이를 반환 (몇개의 블럭이 생성되었는가)

- getLatestBlock : 체인 내의 가장 마지막, 최근 블럭을 반환

- addBlock : 블록체인 배열에 인증된 블럭을 추가하고 블럭 추가의 성공 여부를 반환

- getAdjustmentBlock : 난이도조정을 위해서 난이도를 측정하기 위한 블럭을 반환 

   -> 현재 블럭과 10번째 전 블럭 생성 시간을 비교할 것이므로 10번째 전 블럭을 찾아줘야한다. 

   -> 블럭체인 길이가 10보다 작다면, 제네시스 블럭을 반환하고, 10보다 크다면 10번째 전 블럭 반환해주기

     

더 자세히 말하자면,

11번째 블럭이 생성될 경우 11번째 블럭이 생성된 시간 - 0번째 블럭(제네시스 블럭)이 생성된 시간 = 10*60*10 = 6000초보다 길어지면 난이도를 낮추고 6000초보다 짧아지면 난이도를 높인다.

// src/core/blockchain/chain.ts

import { Block } from "@core/blockchain/block";

export class Chain {
	public blockchain: Block[];
    
    constructor(){
    	this.blockchain = [Block.getGENESIS()];
    }
    
    public getChain(): Block[] {
    	return this.blockchain;
    }
    
    public getLength(): number {
    	return this.blockchain.length;
    }
    
    public getLatestBlock(): Block {
    	return this.blockchain[this.blockchain.length - 1];
    }
    
    public getAdjustmentBlock() {
    	const currentLength = this.getLength();
        const adjustmentBlock: Block = this.getLength() < DIFFICULTY_ADJUSTMENT_INTERVAL
        ? Block.getGENESIS()
        : this.blockchain[currentLength - DIFFICULTY_ADJUSTMENT_INTERVAL];
        return adjustmentBlock;
    }
    
    public addBlock(data: string[]): Failable<Block,string> {
    	const previousBlock = this.getLatestBlock();
        previousBlock.height + 1;
        const adjustmentBlock: Block = this.getAdjustmentBlock();
        const newBlock = Block.generateBlock(previousBlock, data, adjustmentBlock);
        const isValid = Block.isValidNewBlock(newBlock, previousBlock);
        
        if(isValid.isError) return { isError: true, error: isValid.error };
        this.blockchain.push(newBlock);
        
        return { isError: false, value: newBlock };
    }
}

 

 

3. 논스를 포함한 블록 생성

 

generateBlock()

블록체인 내에서 블록을 생성해주는 메소드 

// src/core/blockchain/block.ts - Block 클래스

public static getGENESIS(): Block {
    return GENESIS;
  }
  
public static generateBlock(_previousBlock: Block, _data: string[], _adjustmentBlock: Block): Block {
    const generateBlock = new Block(_previousBlock, _data, _adjustmentBlock);
    // newBlock은 마이닝이 완료된 블럭
    // generateBlock을 가지고 마이닝 관련된 코드를 작성할것
    const newBlock = Block.findBlock(generateBlock);
    return newBlock;
  }

 

findBlock()

논스를 계속 1씩 증가시키면서 난이도와 그 블록해시 앞자리 0개수가 일치할 때의 논스와 해당 해시값을 블록에 업데이트해서 블록을 생성해주는 메서드

이때 블록해시는 기본적으로 16진수이므로 2진수로 바꿔야지만 앞자리 0개수를 확인할 수 있다. 

public static findBlock(_generateBlock: Block): Block {
    let hash: string;
    let nonce: number = 0;
    
    while (true) {
    	nonce++;
        _generateBlock.nonce = nonce;
        hash = Block.createBlockHash(_generateBlock);
        const binary: string = hexToBinary(hash);
        const result: boolean = binary.startsWith("0".repeat(_generateBlock.difficulty));
        
        if (result) {
            _generateBlock.hash = hash;
            console.log('블록생성', binary);
            return _generateBlock;
        }
    }
}

 

4. 블록 난이도 조절하기

 

우선 평균 블록 생성 시간(6000초) 도 정해진건 아니고, 이걸 2로 나누고 곱하는 것 또한 정해진 건 아니다. 

단지 어떤 식으로 대충 난이도를 조절하는지 보여주기 위한 코드이다..실제론 더 복잡할 것이다.

// src/core/blockchain/block.ts - Block 클래스

public static getDifficulty(_newBlock: Block, _adjustmentBlock: Block, _previousBlock: Block): number {
    if (_newBlock.height < 9) return 0;
    if (_newBlock.height < 19) return 1;
    if (_newBlock.height % DIFFICULTY_ADJUSTMENT_INTERVAL !== 0) return _previousBlock.difficulty;
    
    const timeTaken: number = _newBlock.timestamp - _adjustmentBlock.timestamp;
    const timeExpected: number = UNIT * BLOCK_GENERATION_INTERVAL * DIFFICULTY_ADJUSTMENT_INTERVAL; // 6000초
       
       if (timeTaken < timeExpected / 2) return _adjustmentBlock.difficulty + 1;
       else if (timeTaken > timeExpected * 2) return _adjustmentBlock.difficulty - 1;
       
       return _adjustmentBlock.difficulty;
}

 

테스트 코드는 아래와 같다.

 해쉬값을 hexTobinary로 2진수로 변환해보면 난이도 수와 앞의 0 개수가 일치함을 확인할 수 있다.

// src/core/blockchain/chain.test.ts

import { Chain } from "./chain";

describe("chain 함수 체크", () => {
  let node: Chain = new Chain();

  it("getChain() 함수 체크", () => {
    console.log(node.getChain());
  });

  it("getLength() 함수 체크", () => {
    console.log(node.getLength());
  });

  it("getLatestBlock() 함수 체크", () => {
    console.log(node.getLatestBlock());
  });

  it("addBlock 함수 체크", () => {
    for (let i = 1; i <= 200; i++) {
      node.addBlock([`Block #${i}`]);
      console.log(
        `논스값 : ${node.getChain()[i].nonce}, 해쉬값 : ${node.getChain()[i].hash}, 난이도 : ${
          node.getChain()[i].difficulty
        }`,
      );
    }
    console.log(node.getChain());
  });
});

 

Comments