본문 바로가기

블록체인_9기/⛓ BlockChain

57강_230905_Blockchain(chain 생성)

728x90

 

오늘 내용은 전 강의 내용을 이어서 진행한다. 전 강의에서 만든 블록을 이용하여 체인을 생성하고, 조회, 비교, 검사 등을 진행한다.

이전 포스팅의 ' TypeScript로 Block 생성 ' 에서 블록을 생성과 유효성 검사등의 메소드를 작성하였으니 참고하여 확인하도록 하자.

 

56강_230904_Blockchain(SHA256, merkle Tree/Root, Block 생성, TypeScript로 Block 생성)

Hash 함수 SHA256 crypto-js 모듈을 사용하여 SHA256 해시로 암호화하는 방법은 현재 블록체인에서 가장 많이 채택해서 사용하고 있는 암호방식이다. SHA256의 특징은 다음과 같다. 출력속도가 빠르고 단

oo0ojw.tistory.com

 

 

 


Chain 생성_블록의 연관성을 검증하는 Chain 클래스

Chain 클래스는 블록을 저장하고 검색하며, 체인의 일관성을 유지하고 블록체인의 다양한 작업을 수행하는 클래스이다. Chain 클래스의 전체적인 구성은 다음과 같다.

import Block from "@core/block/block";
import { GENESIS } from "@core/config";
import { Failable } from "@core/interface/failable.interface";

class Chain {
  private chain: Block[] = [GENESIS];
  private readonly INTERVAL = 10;

  // 현재 체인을 반환하는 함수
  get() { .... }

  // 길이를 반환하는 함수
  length() { .... }

  // 체인에 마지막 블록 반환 함수
  latestBlock() { .... }

  // 블록 추가 메서드
  addToChain(receivedBlock: Block) { .... }

  // 블록을 조회하는 메서드
  getBlock(callbackFn: (block: Block) => boolean) { .... }

  // 블록의 높이로 블록을 조회하는 함수
  getBlockByHeight(height: number) { .... }

  // 블록의 해시로 블록을 찾는 함수
  getBlockByHash(hash: string) { .... }

  // 현재 위치에서 10번째 블록들을 찾는 함수
  getAdjustBlock() { .... }

  // 다른 네트워크로 체인을 보낼 때
  serialize() { .... }

  // 다른 네트워크에서 체인을 받을 때
  deserialize(chunk: string) { .... }

  // 상대방 체인과 본인의 체인을 비교할 때
  replaceChain(receivedChain: Block[]): Failable<undefined, string> { .... }

  // 현재 블록 생성 시점에서 이전 -10번쩨 블록 구하기
  getAdjustmentBlock() { .... }
}

export default Chain

 

Chain 클래스는 chain과 INTERVAL 속성을 선언했는데, chain 속성은 생성된 블록들을 담는 배열이이며, private 으로 선언되어 직접 참조할 수 없는 속성이다. INTERVAL 속성은 난이도 조절을 위한 블록의 간격을 설정한 속성이다. private readonly 속성으로 선언되어 직접 참조가 불가하고, 선언시 초기화되어 수정할 수 없는 속성이다.

chain 속성의 경우, ' Block[] '이므로 Block 유형의 객체 배열임을 알 수 있고, ' = [GENESIS] ' 이므로 해당 배열의 시작 값은 초기 블록의 값으로 하드코딩했던 GENESIS 값이 들어갔음을 알 수 있다.

INTERVAL 속성의 경우, 간격을 10으로 초기화했음을 확인할 수 있다.

private chain: Block[] = [GENESIS];
private readonly INTERVAL = 10;

 

이 클래스의 메소드들을 하나하나 자세히 살펴보고자 한다.

 

👩‍🏫 get() 메서드_현재 체인(생성된 블록의 배열)을 반환하는 함수

get 메서드는 ' this.chain '의 값을 반환하므로, 현재까지 저장되어있는 체인 배열을 반환한다.

  get() {
    return this.chain;
  }

 

👩‍🏫 length() 메서드_현재 체인(생성된 블록의 배열)의 길이를 반환한다.

length 메서드는 ' this.chain,length '의 값을 반환하므로, 현재까지 저장되어있는 체인 배열의 길이를 반환한다.

  length() {
    return this.chain.length;
  }

 

👩‍🏫 latestBlock() 메서드_현재 체인의 마지막 블록(가장 최근에 생성된 블록)을 반환한다.

laststBlock 메서드는 chain 클래스의 ' this.chain '의 ' this.length() -1 '(chain 배열의 길이 -1 == chain의 마지막 인덱스)의 값을 반환한다.

  latestBlock() {
    return this.chain[this.length() - 1];
  }

 

👩‍🏫 addToChain() 메서드_체인에 생성된 블록을 추가하고 체인에 저장된 마지막 블록을 반환한다.

addToChain 메서드는 매개변수로 체인에 추가할 블록데이터(receivedBlock)를 받는다. chain 클래스에 push해 매개변수로 받은 블록을 체인 배열에 추가한다. latestBlock() 메서드를 사용하여, chain 배열의 마지막 블록(방금 추가한 블록이 체인의 마지막 블록이 될 것이다.)을 반환한다.

  addToChain(receivedBlock: Block) {
    this.chain.push(receivedBlock);
    return this.latestBlock();
  }

 

👩‍🏫 getBlock()/getBlockByHeight()/getBlockByHash() 메서드_매개변수로 받은 조건으로 블록을 조회해 찾은 블록을 반환한다.

getBlock 메서드는 매개변수로 블록을 조회할 조건이 담긴 함수(callbackFn)를 받는다. 함수(callbackFn)는 getBlockByHeight 메서드나, getBlockByHash 메서드에서 반환한 값이다.(

getBlockByHeight는 자신이 매개변수로 받은 height와 chain에 나열되어 있는 블록을 순회하며 동일한 height가 있는지 찾는다.

return this.getBlock((block: Block) => block.height === height)

getBlockByHash는 자신이 매개변수로 받은 hsah와 chain에 나열되어 있는 블록을 순회하며 동일한 hash가 있는지 찾는다.

return this.getBlock((block: Block) => block.hash === hash)

 

두 메서드에서 보내준 함수를 조건으로 chain 배열에서 원하는 함수를 찾고 값을 찾지 못하면 Error를 반환하고, 찾게되면 찾은 블록의 데이터를 반환한다.

  // 블록을 조회하는 메서드
  getBlock(callbackFn: (block: Block) => boolean) {
    const findBlock = this.chain.find(callbackFn);
    if (!findBlock) throw new Error("찾은 블록이 없음");
    return findBlock;
  }

  // 블록의 높이로 블록을 조회하는 함수
  getBlockByHeight(height: number) {
    return this.getBlock((block: Block) => block.height === height);
  }

  // 블록의 해시로 블록을 찾는 함수
  getBlockByHash(hash: string) {
    return this.getBlock((block: Block) => block.hash === hash);
  }

 

👩‍🏫 getAdjustBlock() 메서드_현재 위치에서 특정 위치의 블록을 찾는다.

  getAdjustBlock() {
    const { height } = this.latestBlock();
    const findHeight =
      height < this.INTERVAL
        ? 1
        // INTERVAL == 10
        : Math.floor(height / this.INTERVAL) * this.INTERVAL;
    // 10번째 단위의 블록들의 높이로 블록을 조회해서 블록을 반환?
    return this.getBlockByHeight(findHeight);
  }

 

👩‍🏫 serialize / deserialize() 메서드_다른 네트워크에서 체인을 받고 보낸다.

  // 다른 네트워크로 체인을 보낼 때
  serialize() {
    return JSON.stringify(this.chain);
  }

  // 다른 네트워크에서 체인을 받을 때
  deserialize(chunk: string) {
    return JSON.parse(chunk);
  }

 

👩‍🏫 replaceChain() 메서드_상대방의 체인과 내 체인을 비교한다.

  replaceChain(receivedChain: Block[]): Failable<undefined, string> {
    // 본인의 체인과 상대방의 체인을 검사하는 로직
    // 실제 네트워크에서는 더 복잡한 로직이 들어가 있겠지만,
    // 지금은 체인의 길이를 비교하는 로직을 구현할 것.
    // 전체 배경을 이해하는 것이 중요하다.
    // 실제로는 머클루트, 해시 값, 체인 전체 검증 등의 로직이 더 추가되어 있을 것이다.
    // 중요한 건 체인의 길이를 비교하는 것이다. 롱기스트 체인 룰

    // 상대방의 체인의 마지막 블록을 가져오자
    const latestReceivedBlock: Block = receivedChain[receivedChain.length - 1];

    // 본인 체인의 마지막 블록을 가져오자
    const latestBlock: Block = this.latestBlock();

    // 마지막 블록이 최초블록이라면,
    if (latestReceivedBlock.height === 0)
      return {
        isError: true,
        value: "상대방 네트워크. 체인은 마지막 블록이 최초 블록이다.",
      };

    // 상대방의 마지막 블록의 높이가 내 마지막 블록과 같거나 작다면
    if (latestReceivedBlock.height <= latestBlock.height)
      return {
        isError: true,
        value: "상대방 네트워크의 체인보다 내 체인이 같거나 크다.",
      };

    // 상대방의 체인이 내 체인보다 길면
    // 내 체인을 교체한다. 전달받은 체인으로 업데이트 하는 것이다.
    this.chain = receivedChain;

    return { isError: false, value: undefined };
  }

 

👩‍🏫 getAdjustmentBlock() 메서드_현재 블록 생성 시점에서 이전 -10번째 블록을 구한다.

  getAdjustmentBlock() {
    const currentLength = this.length();
    const adjustmentBlock: Block =
      this.length() < this.INTERVAL
        ? GENESIS
        : this.chain[currentLength - this.length()];

    // 최초블록 or -10번째 블록 반환
    return adjustmentBlock;
  }

 

👩‍🏫 block.test.ts_메서드를 이용한 테스트 코드이다.

it("블록 체인 추가", () => {
    newChain = new Chain();
    newChain.addToChain(newBlock);

    console.log(newChain.get());
    console.log(newChain.getBlockByHeight(1));
    // 블록을 생성될 때마다 해시 값이 바뀌기 때문에 이미 결과로 나온 해시값으로 확인은 어렵다.
    console.log("해시", newChain.getBlockByHash(newBlock.hash));
  });

  it("네트워크 체인 비교(롱기스트 체인 룰)", () => {
    newChain2 = new Chain();
    newChain2.replaceChain(newChain.get());
    console.log(newChain2.get());
  });

  it("이전 10번째 블록 or 최초 블록", () => {
    // 현재 블록을 생성한다 가정하고
    // 현재 블록이 생성된 시간이 이전 10번째 블록으로부터 얼마나 걸렸는지
    // 확인을 하고 블록의 정해진 생성 주기보다 빠르면 난이도를 올리고 아니면 내린다.
    for (let i = 0; i < 20; i++) {
      let block = new Block(GENESIS, ["block"]);
      newChain.addToChain(block);
    }
    console.log(newChain.getAdjustmentBlock());
  });

 

 

728x90