다른 사람이 만든 블럭을 그냥 가져다 쓸 수는 없다.
이 블럭이 제대로 된 블럭인지를 먼저 검증하는 과정이 반드시 필요하다.
다른 사람이 만든 블럭을 봤을 때 이 블럭이 제대로 된 블럭인가? 를 판별하는 기준은 3가지가 있다.
a) 새로운 블럭의 height 값이 이전 블럭의 height + 1 의 값을 가지는가?
b) 이전 블럭의 hash 값과 새로운 블럭의 previousHash 값이 같은가?
c) 새로운 블럭이 가진 data들, 정확히는 version, merkleRoot, timestamp, height, previousHash 값을 조합해
내가 hash값을 만들어봤을 때, 이 블럭이 가진 hash 값이 정말 나오는가?
셋 중 하나라도 만족하지 못하면 이 블럭은 잘못된 블럭이다.
이 검증과정은 간단하게 구현할 수 있다.
검증하는데 필요한 기준이 되는 '이전 블럭' 과 검증할 '새 블럭'을 가져와 속성을 비교하는 함수를 만들면 된다.
이 검증 함수는 Block 자체에 내장되어 있어야 한다.
1. ts 문법 - 조건에 따라 다른 형태를 갖는 type
본격적으로 이를 시작하기 전에 ts의 문법을 활용해 상황에 따라 다른 type을 return해주는 type을 하나 만들어주자.
/* @types/Failable.ts */
declare type Result<R> = { isError : false; value : R }
declare type Failure<E> = { isError : true; error : E }
declare type Failable <R,E> = Result<R> | Failure <E>
Failable type은 Result 혹은 Failure 타입을 가질 수 있는데, 이를 결정하는 것은 isError의 값이다.
만약 Failable type의 isError 값이 true라면 Failure와 같은 형태를 가지게 되며, 반대로 isError 값이 false라면
Result와 동일한 형태를 가지게 된다.
<R>, <E> 는 매개변수로, 우리가 지정해주면 객체 안의 속성값의 데이터 타입을 결정할 수 있다.
가령 Failable < Block, string > 처럼 적어주면
에러가 있어 Failure 형태를 가질 땐 E가 string,
에러가 없어 Result 형태를 가질 땐 R이 Block 타입이 될 것이다.
이를 이용해 블럭 검증 시 에러가 있을 때와 없을때 각각 다른 속성을 가지는 객체를 리턴해줄 수 있다.
2. block 검증 함수
/* src/core/blockchain/block.ts */
public static isValidNewBlock (_newBlock : Block, _previousBlock : Block ) : Failable <Block, string> {
// 조건 a 만족 여부 확인
if(_previousBlock.height + 1 !== _newBlock.hegiht ) {
return { isError : true, error : 'height value error' }
}
// 조건 a 불만족시 Failable<E> 반환
// 조건 a 만족시 조건 b 만족 여부 확인
if ( _previousBlock.hash !== _newBlock.previousHash ) {
return { isError : true, error : 'previousHash value Error' }
}
// 조건 b 불만족시 Failable<E> 반환
// 조건 b 만족시 조건 c 만족 여부 확인
if ( Block.createBlockHash(_newBlock) !== _newBlock.hash ) {
return { isError : true, error : 'hash value error' }
}
// 조건 c 불만족
return { isError : false, value : _newBlock }
// 조건 a,b,c 모두 만족시 Failable<R> 반환
}
이를 Block class 내부에 작성해주고 테스트 항목에 검증 코드를 추가해보자.
/* src/core/blockchain/block.test.ts */
import { Block } from '@core/blockchain/block'
describe('block test', () => {
let newBlock : Block
let newBlock2 : Block
// newBlock, newBlock2 를 전역 변수로 선언
const GENESIS : IBlock = {
version : '1.0.0',
height : 0,
hash : '0'.repeat(64),
timestamp : 1231006506,
previousHash : '0'.repeat(64),
merkleRoot : '0'.repeat(64),
data : ['genesis Block']
}
it('블럭 생성', () => {
const data = ['Blockchain']
newBlock = new Block ( GENESIS, data )
newBlock2 = new Block ( newBlock, data )
// newBlock을 기반으로 newBlock2를 생성
console.log(newBlock)
console.log(newBlock2)
})
it('block 검증', () => {
const isValidBlock = Block.isValidNewBlock (newBlock2, newBlock)
// 이 변수에는 함수 실행 결과 객체가 들어옴.
if (isValidBlock.isError === true) { // error가 있을 경우
console.error(isValidBlock.error)
return expect(true).toBe(false)
// 반드시 틀리는 모순된 명제를 코드로 넣어 error 유도
}
expect(isValidBlock.isError).toBe(false)
테스트 해 문제가 없는지 확인한 후,
newBlock, newBlock2 를 직접 비교해보자.
'Blockchain' 카테고리의 다른 글
#7 mining ch.1 - 개념 (0) | 2022.06.17 |
---|---|
#6 chain (0) | 2022.06.12 |
#4 block 생성 (0) | 2022.06.12 |
#3 typescript - 2 (0) | 2022.06.11 |
#2 typescript (0) | 2022.06.11 |