블록체인 -2 2번째 블록 생성 (23/04/27)

nazzzo·2023년 5월 9일
0

2번째 블록 생성하기


제네시스 블록으로부터 다음 블록을 생성하려면 생성될 블록은 이전 블록에 대한 정보를 참조할 수 있어야 합니다
그리고 이 생성 준비과정에 도달하려면 블록 해시를 제외한 모든 데이터가 완성된 상태여야 합니다


// 두번째 블록 생성을 위한 생성자
class Block {
    constructor() {}

    // 직전 블록 데이터를 의존합니다
    createBlockInfo(previousBlock: IBlock): BlockInfo {
        // const blockInfo: BlockInfo = {
        //     version: VERSION,
        //     height: previousBlock.height + 1,
        //     timestamp: new Date().getTime(),
        //     previousHash: previousBlock.hash,
        //     nonce: 0,
        //     difficulty: 0
        // }
        // return blockInfo;

        // ↓ 속성값을 대입하는 방식이 요즘 트렌드라는데...
        const blockInfo = new BlockInfo()
        blockInfo.version = VERSION
        blockInfo.height = previousBlock.height + 1
        blockInfo.timestamp = new Date().getTime()
        return blockInfo
    }
}

export default Block

1. 블록 검증 함수


블록 생성은 이전 블록 데이터에 대한 신뢰를 전제로 합니다
그래서 새 블록을 생성하기 전에 이전 블록의 해시값이 올바른지에 대한 별도의 검증 과정이 필요합니다

여러 방법론이 있을 수 있겠지만 여기서는 직전 블록의 평문 데이터를 가져와서 또 한 번 해시화를 진행한 뒤
직전 블록이 가진 해시 속성과의 일치여부를 검사하는 방법을 택했습니다


직전 블록의 신뢰성을 검증하는 함수를 생성합니다

 // crypto.createBlockHash() === block.hash ?

    isValidPreviousBlock(previousBlock: IBlock): void {
        this.crypto.isValidHash(previousBlock.hash)

        const validHash = this.crypto.createBlockHash(previousBlock)
        if (validHash !== previousBlock.hash) throw new Error(`이전 블록의 해시값이 올바르지 않습니다 ${validHash} !== ${previousBlock.hash}`)
    }

테스트 코드

describe('Block', () => {
    let block: Block
    let crypto: CryptoModule

    beforeEach(() => {
        crypto = new CryptoModule()
        block = new Block(crypto) // 인스턴스가 crypto의 메서드를 사용할 수 있도록
    })

    describe('isValidPreviousBlock', () => {
        let previousBlock: IBlock
        beforeEach(() => { previousBlock = { ...GENESIS } })

        it(('매개변수로 넘겨받은 블럭 해시값이 올바른가'), () => {
            expect(() => block.isValidPreviousBlock(previousBlock)).not.toThrowError() // not은 반대 입장을 검사
        })
        it(('매개변수로 넘겨받은 블럭 해시값이 올바르지 않을 경우 에러가 발생하는가'), () => {
            previousBlock.hash = "84ffab55c48e36cc480e2fd4c4bb0dc5ee1bb2d41a4f2a78a1533a8bb7df8371"
            expect(() => block.isValidPreviousBlock(previousBlock)).toThrowError()
        })
        it(('블럭 해시값이 변조된 적이 있는가'), () => {
            block.isValidPreviousBlock(previousBlock)
        })
        it(('블럭 해시값이 올바르지 않을 때 에러가 발생하는가'), () => {

        })
    })

    describe(('createBlockInfo'), () => {
        const previousBlock = GENESIS
        it(('createBlockHash 메서드가 존재하는가'), () => {
            expect(typeof block.createBlockInfo).toBe("function")
        })

        it(('createBlock에서 BlockInfo가 잘 생성되는가'), () => {
            const newBlock = block.createBlockInfo(previousBlock)
            expect(typeof newBlock).toBe("object") // failed ~ undefined
        })

        it(('createBlock에서 BlockInfo의 내용이 올바른가'), () => {
            const newBlock = block.createBlockInfo(previousBlock)

            expect(newBlock.previousHash).toBe(previousBlock.hash)
            expect(newBlock.height).toBe(previousBlock.height + 1)
        })
    })
})



2. 블록 생성 함수


2번째 블록을 생성할 함수(createBlock())에는 작업증명(POW)에 관한 로직이 담겨야 합니다
그리고 난이도 조절을 위해서는 10번째로 생성될 블록의 정보도 필요합니다


합의 알고리즘

POW (Proof Of Work): 작업증명
POS (Proof Of Stake): 지분증명
POA (Proof Of Authority): 권한증명


그러면 작업증명을 위한 코드 설계를 시작...

  • 전략 패턴을 사용해서 증명 방식을 쉽게 갈아끼울 수 있는 형태로 클래스를 설계합니다
    같은 인터페이스를 사용하되 내부 로직(POS or POW)만 달라지는 형태로 구현해야 합니다

  • 클래스 설계도 ERD와 마찬가지로 스키마를 그리는 과정이 필요합니다
    이를 UML(Unified Modeling Language)이라고 합니다


UML 예제


테스트 코드 예제

describe('WorkProof', () => {
    let workProof: WorkProof
    let proof: Proof

    describe('POW', () => {
        beforeEach(() => {
            proof = new ProofOfWork()
            workProof = new WorkProof(proof)
        })
        it(('POW 실행'), () => {
            workProof.run()
        })
    })

    describe('POS', () => {
        beforeEach(() => {
            proof = new ProofOfStake()
            workProof = new WorkProof(proof)
        })
        it(('POS 실행'), () => {
            workProof.run()
        })
    })
})



POW 방식으로 블록 100개 생성하기

// 제네시스 블록을 체인(블록 배열)에 담은 상태에서 시작합니다
const BlockList:IBlock[] = [ GENESIS ]


for (let i = 1; i < 100; i++) {
    const adjustmentBlock = (i >= 19) ? BlockList[Math.floor(i / 10)*10-10] : GENESIS
    // 3번째 인자를 통해 난이도 조절. (20-29번 블록의 생성 난이도는 10번째 블록을 기준으로 합니다)
    const bitcoin = block.createBlock(BlockList[i-1], "data", adjustmentBlock)
    BlockList.push(bitcoin)
}

console.log(BlockList)


/**
[
  {
    version: '1.0.0',
    height: 0,
    timestamp: 1231006506,
    previousHash: '0000000000000000000000000000000000000000000000000000000000000000',
    merkleRoot: 'DC24B19FB7508611ACD8AD17F401753670CFD8DD1BEBEF9C875125E98D82E3D8',
    nonce: 0,
    difficulty: 0,
    hash: '84ffab55c48e36cc480e2fd4c4bb0dc5ee1bb2d41a4f2a78a1533a8bb7df8370',
    data: '2009년 1월 3일 더 타임스, 은행들의 두번째 구제금융을 앞두고 있는 U.K 재무장관'
  },
  {
    nonce: 1,
    difficulty: 0,
    version: '1.0.0',
    height: 1,
    timestamp: 1682640185673,
    previousHash: '84ffab55c48e36cc480e2fd4c4bb0dc5ee1bb2d41a4f2a78a1533a8bb7df8370',
    merkleRoot: '3A6EB0790F39AC87C94F3856B2DD2C5D110E6811602261A9A923D3BB23ADC8B7',
    data: 'data',
    hash: 'a07aef620d2ba153b5173a42371db8fbd2da5873eecc82c0ef89890461c6d0e1'
  },
  ...
  {
    nonce: 3,
    difficulty: 2,
    version: '1.0.0',
    height: 100,
    timestamp: 1682640185677,
    previousHash: '16b455db348f232bba09ca26ac21d798ff323390ef1bf468abaef573843f9f2c',
    merkleRoot: '3A6EB0790F39AC87C94F3856B2DD2C5D110E6811602261A9A923D3BB23ADC8B7',
    data: 'data',
    hash: '335fb98039f66c68f16e9a2a761bb2d9dd2a08bba90b7d7c62bfe2d54b58c21e'
  }
]
*/  

0개의 댓글