typescript 체인 시스템 추가

이무헌·2023년 9월 5일
0

blcokchain

목록 보기
3/10
post-thumbnail

1.체인 시스템 추가

이전글에서 다뤘던 블록체인 시스템 구현 부분에서 블록들을 담은 체인을 추가할 것이다. 각 체인안의 메서드는 다음과 같은 기능들을 담고있다.

  • 체인을 확인 할 수 있는 메서드
  • 체인의 길이를 반환하는 메서드
  • 체인의 마지막 블록을 반환하는 함수
  • 체인에 블록을 추가하는 함수
  • 특정 블록을 조회하는 함수
  • 블록의 높이로 블록을 조회하는 함수
  • 블록의 해시로 찾는 함수
  • 10번째 블록을 찾는 함수
  • 다른 네트워크에서 체인을 받을 때이를 parse함수
  • 다른 네트워크로 체인을 보낼 때 chain을 stringfy 해주는 함수
  • 상대방 체인과 본인의 체인을 비교해서 longest rule을 적용시켜주는 함수
  • 10번째 블록을 현재 블록과 생성시간을 비교해서 난이도를 조정하는 함수

2.전체코드

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

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

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

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

  //   체인에 마지막 블록 반환 함수
  latestBlock() {
    return this.chain[this.length() - 1];
  }

  //   블룩 추가 메서드
  addToChain(receiveBlock: Block) {
    this.chain.push(receiveBlock);
    return this.latestBlock();
  }

  //   블록을 조회하는 메서드
  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);
  }
  //   10번째 블록들을 찾는 함수 현재 위치에서
  getAdjustBlock() {
    const { height } = this.latestBlock();
    const findHeight =
      height < this.INTERVAL
        ? 1
        : Math.floor(height / this.INTERVAL) * this.INTERVAL;
    // 10번째들의 블록읠 높이로 블록을 조회해서 블록 반환
    return this.getBlockByHeight(findHeight);
  }
  //   다른 네트워크로 체인을 보낼 때
  serialize() {
    return JSON.stringify(this.chain);
  }

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

    // 상대방의 체인의 마지막 블록
    const latestReceivedBLock: Block = receiveChain[receiveChain.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 = receiveChain;
    return { isError: false, value: undefined };
  }

  // 추가할 블록을 찾으면 네트워크에 브로드 케스트를 하고
  // 다른 네트워크들은 내 체인과 블록을 받아요
  // 블록 검증을 하고
  // 체인검증을 하는데
  // 다른 네트워크의 체인과 내 체인을 비교해서 긴 체인이 정답
  // 다른 네트워크의 체인이 더 길 경우에는 내가 채굴이 늦은 것 (x)
  // 다른 네트워크의 체인보다 길어지면 내가 채굴을 더 빠르게 한거=> 보상(o)

  // 전체 블록 생성 시점에서
  // 이전 -10 번째 블록 구하기
  // 현재 높이가 <10:최초블록을 반환하고
  // 현재 높이가>10:-10번째 블록을 반환
  // 이전 10번째 블록의 생성 시간의 차이를 구해서
  // 그 차이가 블록생성 주기보다 빠르면 난이도를 증가
  // 느리면 난이도 증가
  // 비트코인 기준 생성시간 10분, 10개가 생성되면 100분
  // 100분보다 빠르면 난이도 상승, 느리면 하락

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

export default Chain;

1.get()

현재 체인을 반환한다.

2.length()

현재 체인의 길이를 반환한다.

3.latestBlock

체인의 마지막 블록을 반환한다.

4.addToChain(receiveBlock: Block)

블록을 받아 추가한다.

5.getBlock(callbackFn: (block: Block) => boolean)

콜백함수를 받아 해당 콜백함수의 return 값이 존재할 경우 해당 블록을 찾아준다.
이 콜백들은 하위에서 서술하겠다

6.getBlockByHeight(height: number)

블록의 높이로 블록을 조회하며 5번의 getBLock에 콜백함수로 높이 비교를 해주었다.

7.getBlockByHash(hash: string)

마찬가지로 콜백함수로 해시비교를 해 해당 해시값의 블록을 반환한다.

8.getAdjustBlock()

10번째 블록의 높이를 반환한다. 만약 10번째까지 없다면 1을 반환한다.

9.serialize(), deserialize(chunk: string)

각각 체인을 문자화,JSON화 하여 네트워크로 보내고 받는다.

10.replaceChain(receiveChain: Block[]): Failable<undefined, string>

longest rule을 적용하기 위해 상대방의 체인과 나의 체인을 비교한다.
내가 상대방의 체인보다 길면 에러로 자신의 체인이 길거나 같다고 반환하고 그것이 아니라면 내 체인에 상대방의 체인을 삽입한다.

11.getAdjustmentBlock()

현재 기분 10번째전 블록을 반환한다. 이를 기준으로 걸린시간을 계산해서 난이도를 조정한다.

3.jest.config.ts 변경

이전 포스트에서 작성된 코드를 그대로 복붙해서 설정을 변경해줘야한다.

import type { Config } from "@jest/types";

const config: Config.InitialOptions = {
  // 1.모듈 파일 확장자 설정: typescript와 javascript 둘 다 테스트 파일로 지정
  moduleFileExtensions: ["ts", "js"],
  //   2.테스트 파일 매치 설정: 파일의 이름의 패턴을 설정
  //  루트 경로에서 모든 폴더에 모든 파일 이름의 패턴이 test.js or test.ts
  testMatch: ["<rootDir>/**/block2.test.(js|ts)"],
  // 3.모듈의 이름에 대한 별칭 설정:@core
  //   뱔칭으로 지정된 @core를 어떻게 경로를 바꿔줄거냐
  // ^@core==@core/**/* 시작하는 별칭은 루트 경로에 src/core의 경로까지

  moduleNameMapper: {
    // rootDir는 ts_lecture
    "^@core/(.*)$": "<rootDir>/20230904/02.Block/src/core/$1",
    "^@coreChain/(.*)$": "<rootDir>/20230904/03.chain/src/core/$1",
  },

  //   4.테스트 환경 설정:node환경에서 실행 시킬거임
  testEnvironment: "node",
  // 5.자세한 로그 설정 출력: 터미널에 로그들을 더 자세히 출력할지 여부
  verbose: true,
  // 6.프리셋 설정:typescript 에서 사용랄 jest/ts-jest설정
  preset: "ts-jest",
};
export default config;

moduleNameMapper에서 "^@coreChain/(.)$": "<rootDir>/20230904/03.chain/src/core/$1",부분이 추가 됐다.
별칭 하나를 추가해야 새로운 폴더에 작성된coreChain별칭을 사용가능하다.
testMatch: ["<rootDir>/**/block2.test.(js|ts)"],
이 부분은 테스트 코드를 위에서 읽기 시작할 때block2를
와일드카드로 주었을시 첫번째 테스트만 실행되는 버그가 있었다. 때문에 따로 block2파일만을 실행하도록 하였다.

4.느낀점

모르는게 산더미로 밀려오지만 나름 기초공부를 해서 잘 따라가고 있다. 시간될 때 마다 복습과 예습을 하여 개념만이라도 익혀야 겠다.... 특히 블록의 헤더구조와 같은 개념적인 부분은 머릿속에서 자연스럽게 떠올릴 정도로 봐야한다.

profile
개발당시에 직면한 이슈를 정리하는 곳

0개의 댓글