어제는 마이닝을 통해 블록을 생성을 하는 것을 배웠다면,
오늘은 그 블록들을 배열로 가지고 있는 체인을 다뤄보자.
import Block from "@core/block/block"
import { GENESIS } from "@core/config"
import { Failable } from "@core/interface/failable.interface"
class Chain{
//⭐⭐⭐⭐⭐⭐⭐
// chain 은 배열이고, 배열의 요소들은 Block 타입이여야한다.
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(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};
}
// 현재 블록 생성 시점에서
// 이전 -10번째 블록 구하기
// 현재 높이값 < 10 : 최초블록을 반환하고
// 현재 높이값 > 10 : -10번째 블록 반환.
// 이전 10번째 블록의 생성 시간의 차이를 구해서
// 그 차이가 블록 생성 주기 보다 빠르면 난이도를 증가
// 생성주기가 느리면 난이도 하락
// 비트코인 기준으로 블록의 생성 시간은 10분에 1개
// 10개가 생성되는 시간은 100분
// 100분 보다 빠르면 난이도를 상승시키고
// 100분도 보다 느리면 난이도를 하락시킨다.
getAdjustmentBlock(){
// 내 체인의 길이
const currentLength = this.length();
const adjustmentBlock : Block = this.length() < this.INTERVAL ? GENESIS : this.chain[currentLength-this.INTERVAL];
// 최초블럭 or -10번째 블록반환
return adjustmentBlock;
}
}
export default Chain;
let findHeight =1;
getBlockByHeight(findHeight); 코드로
getBlockByHeight(height: number){
//number 에는 1
return this.getBlock((block : Block)=> block.height === height);
} 이 함수가 호출되고,
//⭐⭐⭐⭐⭐⭐⭐
//getBlock 함수의 매개변수는 callback 함수가 들어가야하고,
//매개변수는 block 이고 Block 타입을 가지고 반환값은 boolean 값이여한다.
//전달한 매개변수는 Block 타입이고, 반환값은 block.height === height 코드로 bool 이다.
//그래서 bool 값이 true 면 find 함수가 height가 일치하는 요소를 찾아 findBlock에 반환한다.
getBlock(callbackFn : (block : Block) => boolean){
const findBlock = this.chain.find(callbackFn)
// findBlock 의 값이 없으면 아래 코드 실행.
if(!findBlock) throw new Error("찾은 블록이 없음");
return findBlock;
} 이 함수까지 호출된다.
it("블록 체인 추가", ()=>{
newChain = new Chain();
newChain.addToChain(newBlock);
})
it("네트워크 체인 비교(롱기스트 체인 룰)",()=>{
newChain2 = new Chain();
newChain2.replaceChain(newChain.get());
})
it("이전 10번째 블록 or 최초 블록 ", ()=>{
// 현재 블록을 생성한다 가정하고
// 현재 블록이 생성된 시간이 이전 10번째 블록으로부터 얼마나 걸렸는지
// 확인을하고 블록의 정해진 생성주기보다 빠르면 난이도를 올리고 아니면 내린다.
// console.log("latestBlock---------------------",newChain.latestBlock());
for (let i = 0; i < 20; i++) {
let block = new Block(newChain.latestBlock(),["block"])
newChain.addToChain(block);
}
})