목차
- 용어정리
- 대표적인 합의 알고리즘
- 채굴(mining) 구현
1. 용어 정리
- 노드 : 개개인의 서버. 블록체인 네트워크의 참여자들
- 지갑: 개인키/공개키를 관리하는 프로그램. 거래에 사용되는 주소/계정 생성
- 해시캐시
- 특정 조건을 충족하는 해시를 찾아내는 일련의 과정
- 해시값이 몇 개 이상의 0으로 시작하는 값을 찾는 작업
- 합의 알고리즘
- 분산원장 시스템 내의 모든 노드(node)가 일관성 있는 분산원장을 보유할 수 있도록 네트워크 연결을 통해 새로운 기록의 공유, 검증 및 추가에 대한 전체의 동의를 이끌어 내는 알고리즘
- 노드 간의 분산원장 동기화는 동시에 일어나지 않기 때문에 네트워크상에는 일시적으로 서로 다른 기록이 포함된 원장들이 존재할 수 있고 노드 중에는 신뢰할 수 없는 참여자가 포함될 수 있다고 가정하는 상황에서도 분산원장 시스템 내의 노드들이 서로 간의 통신을 통해 최종적으로 동일한 기록을 채택하게 하는 방식
- 채굴
- POW 방식으로 경쟁하여 블록 생성하는 작업을 통해 암호화폐를 소유하는 것
- 난이도에 일치하는 블록 해시를 찾을 때까지 nonce 값을 변경시키는 것
- 블록체인에 새로운 블록을 추가하는 일련의 과정
- nonce
- 네트워크의 난이도 목표를 달성하기 위해 충분한 수의 제로비트를 가진 해시를 산출
- 블럭 생성 실패할 경우 값을 +1 해줘서 해시값을 새로 생성
2. 대표적인 합의 알고리즘
합의 알고리즘 | 설명 |
작업증명(POW) | 목표값 이하의 해시를 찾는 과정(해시캐시)을 무수히 반복함으로써 해당 작업에 참여했음을 증명하는 방식의 합의 알고리즘 거래 기록 정보를 무작위 특성을 가진 nonce 값과 해시 알고리즘을 적용시켜 설정된 난이도를 충족하는 해시값 도출 hex인 해시값을 binary로 변환했을 때 앞에 0이 몇개 붙었는가를 찾는 것이 작업증명(POW) |
지분증명(POW) | 암호화폐를 보유하는 지분율에 비례 노드가 보유한 자산을 기준으로 권한을 분배하여 합의, 보상 분배 지분: 자신의 ID에 연결된 지분의 양 즉, 하나의 ID에 모든 지분을 연결하는 것이 유리하다 |
위임지분증명(DPOS) | 각자의 지분율에 비례한 투표를 통해 자신을 대신하는 대표 노드 선정, |
3. 마이닝 구현
마이닝 구현을 위하여
- 체인 구성과 관련된 상수를 변수화하기
- 체인 구현
- 난이도 구현
- 마이닝 구현
을 해보자
1) 체인 구성과 관련된 상수를 변수화하기
/* src/core/config.ts */
/**
*난이도 조절 블록 범위
*/
export const DIFFICULTY_ADJUSTMENT_INTERVAL: number = 10;
/**
*개당 블록 생성시간(단위: 분)
*/
export const BLOCK_GENERATION_INTERVAL: number = 10;
/**
* 생성시간 단위(단위: 초)
* */
export const UNIT: number = 60;
2) 체인 구현
import { Block } from '@core/blockchain/block';
import { DIFFICULTY_ADJUSTMENT_INTERVAL, GENESIS } from '@core/config';
export class Chain {
private blockchain: Block[];
//퍼블릭으로 함수 메서드를 전부 생성해놨기 때문에 구지 blockchain을 public으로 가져올 필요 없음
constructor() {
this.blockchain = [Block.getGENESIS()];
}
public getChain(): Block[] {
return this.blockchain;
}
public getLength(): number {
return this.blockchain.length;
}
public getLatestBlock(): Block {
return this.blockchain[this.blockchain.length - 1];
}
// 받은 블럭에 대한 유효성 검증 후 체인에 추가하기 위한 메서드
addToChain(_receivedBlock: Block): Failable<undefined, string> {
const isValid = Block.isValidNewBlock(_receivedBlock, this.getLatestBlock());
if (isValid.isError) return { isError: true, error: isValid.error };
this.blockchain.push(_receivedBlock);
return { isError: false, value: undefined };
}
public isValidChain(_chain: Block[]): Failable<undefined, string> {
//제네시스 블럭을 검사하는 코드가 들어가면 됨
const genesis = _chain[0];
//모든 블럭을 하나하나 다 검증해서 false인 지 true인 지 확인
for (let i = 1; i < _chain.length; i++) {
const newBlock = _chain[i];
const previousBlock = _chain[i - 1];
const isValid = Block.isValidNewBlock(newBlock, previousBlock);
if (isValid.isError) return { isError: true, error: isValid.error };
}
return {
isError: false,
value: undefined,
};
}
replaceChain(receivedChain: Block[]): Failable<undefined, string> {
//내 체인과 상대방 체인에 대해서 검사해서 내 체인을 변화시킬 이유가 없을 경우 함수 return
//1. 받은 체인의 길이 === 1 ....(받은 체인의 최신블럭의 높이가 0) 상대가 제네시스 밖에 없는 경우 return
//2. 받은 체인의 최신 블럭 height와 내 체인의 최신블록 height를 비교해서, 내 height가 더 길면 내가 더 최신블록이므로 return
//2. 받은 최신블록.height <= 내 체인 최신블록.height이면 return
//3. 받은 최신블럭의 previousHash 값 === 내 체인의 hash값
//4. 여기까지왔다면 내 체인이 더 짧다....체인 바꾸는 코드 실행
//블럭 가져오기
const latestReceivedBlock: Block = receivedChain[receivedChain.length - 1];
const latestBlock: Block = this.getLatestBlock();
//위의 1~3 조건문 작성
if (latestReceivedBlock.height === 0) {
return { isError: true, error: '받은 최신블록이 제네시스 블록입니다' };
}
if (latestReceivedBlock.height <= latestBlock.height) {
return { isError: true, error: '자신의 블록이 더 길거나 같습니다' };
}
if (latestReceivedBlock.previousHash === latestBlock.hash) {
return { isError: true, error: '블록이 하나만큼 모자랍니다' };
}
//체인을 바꿔주는 코드를 작성
this.blockchain = receivedChain;
return {
isError: false,
value: undefined,
};
}
/**
* 생성기준으로 블록높이가 -10짜리 구해오기
*/
public getAdjustmentBlock() {
//난이도를 측정하는 블록의 단위를 상수로 정할 거임
//현재 배열의 마지막 블럭에서 -10(DIFFICULTY_ADJUSTMENT_INTERVAL)
const currentLength = this.getLength(); //length로 height값 대체
const adjustmentBlock: Block =
currentLength < DIFFICULTY_ADJUSTMENT_INTERVAL
? Block.getGENESIS()
: this.blockchain[currentLength - DIFFICULTY_ADJUSTMENT_INTERVAL];
return adjustmentBlock; //height에 해당하는 블럭 자체를 반환
}
}
3) 난이도구현
public static getDifficulty(_newBlock: Block, _adjustmentBlock: Block, _previousBlock: Block): number {
if (_adjustmentBlock.height === 0) return 0;
// 시간을 구할 때 10번째 배수의 블록일때만 검사
if (_newBlock.height < 9) return 0;
if (_newBlock.height < 19) return 1;
if (_newBlock.height % DIFFICULTY_ADJUSTMENT_INTERVAL !== 0) return _previousBlock.difficulty;
// 시간 계산
const timeTaken: number = _newBlock.timestamp - _adjustmentBlock.timestamp;
const timeExpected: number = UNIT * BLOCK_GENERATION_INTERVAL * DIFFICULTY_ADJUSTMENT_INTERVAL;
//1분 * 10개 * 10분에 1개
//상수를 어떤 값으로 만들었는 지 변수를 통해 계산해줘야 함, 특히 시간 부분
//기준을 생성 기대 시간의 2배로 잡았다고 가정
if (timeTaken < timeExpected / 2) return _adjustmentBlock.difficulty + 1;
if (timeTaken > timeExpected * 2) return _adjustmentBlock.difficulty - 1;
return _adjustmentBlock.difficulty;
}
4) 마이닝 구현
/* index.ts */
const ws = new P2PServer();
// 블록 채굴 api
app.post('/mineBlock', (req, res) => {
const { data } = req.body;
const newBlock = ws.addBlock(data);
if (newBlock.isError) return res.status(500).json(newBlock.error);
res.json(newBlock.value);
});
/* src/core/blockchain/chain.ts */
public addBlock(_data: string[]): Failable<Block, string> {
//난이도 구하기 위해서는 현재 블럭과 -10 블럭의 생성시간을 비교하는 코드를 구현해야 함
const previousBlock = this.getLatestBlock();
const adjustmentBlock = this.getAdjustmentBlock(); //-10 블럭 구함
const newBlock = Block.generateBlock(previousBlock, _data, adjustmentBlock);
const isValid = Block.isValidNewBlock(newBlock, previousBlock);
if (isValid.isError) return { isError: true, error: isValid.error };
this.blockchain.push(newBlock); //함수의 목적
return { isError: false, value: newBlock };
}
'블록체인' 카테고리의 다른 글
이더리움 생태계 (0) | 2022.06.27 |
---|---|
transaction 생성 (0) | 2022.06.22 |
P2P 통신을 통해 노드 간 체인 주고받기[수정중] (0) | 2022.06.20 |
P2P 통신으로 노드 간 블록 주고받기 (0) | 2022.06.14 |
이론1. 블록체인 개념 (0) | 2022.06.08 |
댓글