이전 포스팅까지의 내용은 클라이언트 쪽 역할을 하는 A 노드와 서버 쪽 역할을 하는 B 노드가 서로 메세지를 주고 받을 수 있는 상태를 만들기까지의 작업이었다. 현재 A 노드와 B 노드는 어떠한 메세지를 받았는지에 따라 블록 체인 상에 블록을 추가할지 , 체인 자체를 교체할지 결정할 수 있는 상태이다. 이번 포스팅에서는 체인에 블록 추가할지 체인을 교체할지 검증하는 과정들에 대해 다뤄보고자 한다.
이전 글)
2022.06.15 - [분류 전체보기] - BlockChain - 블록체인 P2P 네트워크 만들기 (2)
현재 p2p.ts 파일은 다음과 같다.
// p2p.ts 파일 (중간 완성본)
import { WebSocket } from "ws";
import { Chain } from "@core/blockchain/chain";
enum MessageType {
latest_block = 0,
all_block = 1,
receivedChain = 2,
}
interface Message {
type: MessageType;
payload: any;
}
export class P2PServer extends Chain {
private sockets: WebSocket[];
constructor() {
super();
this.sockets = [];
}
listen() {
const server = new WebSocket.Server({ port: 7545 });
// 서버 기준 "connection"
server.on("connection", (socket) => {
console.log("webSocket connected");
this.connectSocket(socket);
});
}
connectToPeer(newPeer: string) {
const socket = new WebSocket(newPeer);
// 클라이언트 기준 "open"
socket.on("open", () => {
this.connectSocket(socket);
});
}
connectSocket(socket: WebSocket) {
// 향후 broadcasting을 하기 위한 용도
this.sockets.push(socket);
this.messageHandler(socket);
const data: Message = {
type: MessageType.latest_block,
payload: {},
};
const send = P2PServer.send(socket);
send(data);
}
messageHandler(socket: WebSocket) {
const callback = (_data: string) => {
const result: Message = P2PServer.dataParse<Message>(_data);
const send = P2PServer.send(socket);
switch (result.type) {
// type에 따라 다른 처리를 해준다.
case MessageType.latest_block: {
const message: Message = {
type: MessageType.all_block,
payload: [this.getLatestBlock()],
};
send(message);
break;
}
case MessageType.all_block: {
const message: Message = {
type: MessageType.receivedChain,
payload: this.getChain(),
};
// ToDo : 체인에 블록을 추가할지 말지 결정
send(message);
break;
}
case MessageType.receivedChain: {
const receivedChain: IBlock[] = result.payload;
// ToDo : 체인을 교체하는 코드 (보다 긴 체인 선택하기)
console.log(receivedChain);
break;
}
}
};
socket.on("message", callback);
}
static send(_socket: WebSocket) {
return (_data: Message) => {
_socket.send(JSON.stringify(_data));
};
}
static dataParse<T>(_data: string): T {
return JSON.parse(Buffer.from(_data).toString());
}
}
messageHandler( ) 메소드 안에서 switch 문을 통해 전달받은 데이터의 type 속성에 따라 각기 다른 처리를 해주는 코드를 작성하고자 한다.
클라이언트 쪽 역할을 하는 A노드에서 서버 쪽 역할을 하는 B 노드에게 소켓 연결을 시도한다. 소켓 연결이 성공적으로 완료되어 B 노드에서 "connection" 이벤트가 발생하고 콜백함수 안에 있는 connectSocket( ) 함수가 실행된다. connectSocket( ) 함수 안에서 send( data ) 함수를 통해 A 노드에게 데이터를 전달한다.
connectSocket(socket: WebSocket) {
// 향후 broadcasting을 하기 위한 용도
this.sockets.push(socket);
this.messageHandler(socket);
const data: Message = {
type: MessageType.latest_block,
payload: {},
};
const send = P2PServer.send(socket);
send(data);
}
A 노드에게 전송한 data 객체는 type 속성의 값이 MessageType.latest_block 인 데이터이다. A 노드에서는 messageHandler( ) 함수 안에 있는 switch 문에 의해 다음의 코드블록이 실행된다.
case MessageType.latest_block: {
const message: Message = {
type: MessageType.all_block,
payload: [this.getLatestBlock()],
};
send(message);
break;
}
P2PServer 클래스는 Chain 클래스를 상속받고 있기 때문에 Chain 클래스 안에서 정의된 this.getLatestBlock( ) 메소드를 이용해 블록체인 상에서 가장 최신 블록을 가져올 수 있다. A 노드에서는 해당 블록을 payload 값으로 하고 type 속성값을 MessageType.all_block으로 하는 message 객체를 만들어서 B 노드에게 전송한다.
이제 B 노드에서는 messageHandler( ) 함수 안에 있는 switch 문의 case들 중 MessageType.all_block에 해당하는 코드블록이 실행된다.
case MessageType.all_block: {
const message: Message = {
type: MessageType.receivedChain,
payload: this.getChain(),
};
// ToDo : 체인에 블록을 추가할지 말지 결정
send(message);
break;
}
B 노드는 현재 A 노드에 있는 블록체인 상에서의 최신 블록을 받아온 온 상태이다. B 노드는 이제 case MessageType.all_block: { } 코드블록 안에서 A 노드에서의 최신 블록을 자신의 블록체인 상에 추가할지 결정하여야 한다. 따라서 Chain 클래스 안에서 addToChain( ) 이라는 메소드를 추가로 만들어주었으며 해당 메소드를 통해 검증 과정을 진행하였다.
case MessageType.all_block: {
const message: Message = {
type: MessageType.receivedChain,
payload: this.getChain(),
};
// ToDo : 체인에 블록을 추가할지 말지 결정
const [receivedBlock] = result.payload; // [this.getLatestBlock()]
const isValid = this.addToChain(receivedBlock);
// addToChain이 성공했을 때는 추가적인 요청이 불필요. -> break
if (!isValid.isError) break;
send(message);
break;
}
// Chain 클래스 안에 추가된 메소드
public 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 };
}
Chain 클래스 안에서 새롭게 정의한 addToChain( ) 메소드 안에서 Block 클래스의 isValidNewBlock( ) 메소드를 이용해 검증과정을 거치게 된다.
// Block 클래스 안의 isValidNewBlock() 메소드
public static isValidNewBlock(_newBlock: Block, _previousBlock: Block): Failable<Block, string> {
if (_previousBlock.height + 1 !== _newBlock.height) return { isError: true, error: 'height error' };
if (_previousBlock.hash !== _newBlock.previousHash) return { isError: true, error: 'previousHash error' };
if (Block.createBlockHash(_newBlock) !== _newBlock.hash) return { isError: true, error: 'block hash error' };
return { isError: false, value: _newBlock };
}
isValidNewBlock( ) 함수는 A 노드에게서 전달 받은 A 노드 상의 최신블록(receivedBlock)과 B 노드상의 최신블록을 인자값으로 받아 두 블록의 높이 차이가 1만큼 나는지, receivedBlock의 previousHash 값과 B 노드상 최신블록의 hash 값이 같은지, receivedBlock의 hash 값이 올바른지를 검증한다. 다시말해, A 노드 상의 블록체인 길이가 B 노드 상의 블록체인 길이 보다 1만큼 길때 A 노드 상의 최신블록과 B 노드 상의 마지막 블록을 isValidNewBlock( ) 함수를 사용해서 비교해주는 것이다. isValidBlock( ) 함수가 { isError: false, value: receivedBlock } 을 return 하면 검증을 통과한 것이고 addToChain( ) 메소드 안에 있는 this.blockchain.push( _receivedBlock } 에 의해 B 노드 상의 블록체인에 A 노드 상의 최신블록이 추가된다.
만약 검증에 실패하여 isValidNewBlock( ) 함수가 { isError: true, error: " " }을 return 하게 되면 addToChain( ) 메소드 안에 있는 if (isValid.isError) return { isError: true, error: isValid.error } 에 의해 addToChain( ) 함수가 종료된다.
다시 case MessageType.all_block: { } 코드블록으로 돌아와서, B 노드 상의 블록체인에 A 노드 상의 최신블록을 추가하지 못했을 경우 isValid 변수에는 addToChain( ) 함수의 return 값인 { isError: true, error: isValid.error } 가 할당되고 send( message ) 함수가 실행된다. send( ) 함수의 인자값으로 들어간 message 변수는 다음의 객체이다.
const message: Message = {
type: MessageType.receivedChain,
payload: this.getChain(),
};
즉, B 노드는 자신의 블록체인 전체를 payload 값으로 하는 message 객체를 A 노드에게 전송하게 되는 것이다.
B 노드에게서 전송된 데이터로 인해 이제 A 노드에서는 case MessageType.receivedChain 에 해당하는 코드블록이 실행되게 된다.
case MessageType.receivedChain: {
const receivedChain: IBlock[] = result.payload;
// ToDo : 체인을 교체하는 코드 (보다 긴 체인 선택하기)
console.log(receivedChain);
break;
}
A 노드는 B 노드 상에 있는 블록체인 전체를 payload 값으로 하는 데이터 객체를 전달 받은 상태이다. A 노드 상의 블록체인에서 생성된 최신 블록이 B 노드 상의 블록체인에 추가되지 못한 상황이 발생했다는 것은 A 노드에서 생성된 블록이 올바른 블록이 아니라는 의미이다. 결국 A 노드는 자신의 블록체인을 B 노드의 블록체인으로 교체해야만 하는 상황에 놓이게 된다.
다음은 수정된 case MessageType.receivedChain: { } 코드블록이다.
case MessageType.receivedChain: {
const receivedChain: IBlock[] = result.payload;
// ToDo : 체인을 교체하는 코드 (보다 긴 체인 선택하기)
this.handleChainResponse(receivedChain);
break;
}
P2PServer 클래스 안에서 handleChainResponse( ) 메소드를 만들어준 다음 case MessageType.receivedChain { } 코드블록 안에서 handleChainResponse( ) 함수를 실행시켜 주었다. handleChainResponse( ) 함수 안에서는 B 노드에게서 전달 받은 블록체인이 올바른 체인인지 검증해주는 코드와 A 노드의 블록체인을 B 노드의 블록체인으로 교체할지 결정하는 코드를 구현하고자 한다.
// P2PServer 클래스 안에 추가된 메소드
handleChainResponse(receivedChain: IBlock[]): Failable<Message | undefined, string> {
const isValidChain = this.isValidChain(receivedChain)
if (isValidChain.isError) return {isError: true, error: isValidChain.error}
const isValid = this.replaceChain(receivedChain);
if (isValid.isError) return { isError: true, error: isValid.error };
// broadcast 부분
const message: Message = {
type: MessageType.receivedChain,
payload: receivedChain,
};
this.broadcast(message);
return {isError: false, value: undefined}
}
handleChainResponse( ) 메소드 안에서는 우선 isValidChain( ) 메소드를 사용해 B 노드로부터 전달받은 receivedChain 을 검증해주게 된다. isValidChain( ) 메소드는 Chain 클래스 안에서 다음과 같이 구현해주었다.
// Chain 클래스 안에 추가된 메소드
// 체인 검증 코드
public isValidChain(_chain: Block[]): Failable<undefined, string> {
// ToDo : 제네시스 블록을 검사하는 코드
// const genesis = _chain[0]
// ToDo : 나머지 체인에 대한 검증 코드
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 };
}
isValidChain( ) 함수 안에서 전달받은 B 노드 상의 블록체인(receivedChain)이 올바른 블록체인인지 검사해주게 되는데 검증해야할 것은 크게 두가지이다. 우선 receivedChain의 제네시스 블록을 검증하고 제네시스 블록을 제외한 나머지 블록들에 대한 검증을 거쳐야한다. 위의 코드에서는 제네시스 블록에 대한 검증 부분은 생략하였는데 해당 부분은 방법만 간단히 언급하고 넘어가도록 하겠다. 제네시스 블록 검증에서는 JSON.stringify( ) 메소드를 사용해 A 노드 상의 제네시스 블록과 receivedChain 의 제네시스 블록을 string으로 변환한 다음 서로 같은지 비교해주면 된다.
제네시스 블록을 제외한 나머지 블록들은 for 문을 이용해 연결된 블록들이 올바른 블록인지 Block.isValidNewBlock( ) 메소드를 사용해 검증해주면 된다. 만약 올바르지 않은 블록이 있을 경우 isValidChain( ) 함수는 return 값으로 { isError: true, error: isValid.error } 를 갖게 되고 handleChainResponse( ) 메소드는 if 문에 의해 종료된다.
전달 받은 receivedChain 에 대한 검증이 성공적으로 완료되었다면 이제 A 노드 상에 존재하는 블록체인을 receivedChain으로 교체할지 결정해줘야 한다. 체인을 교체하는 과정의 코드는 this.replaceChain( receivedChain ) 메소드에 의해 실행되는데 해당 메소드 역시 Chain 클래스 내에서 다음과 같이 만들어주었다.
// Chain 클래스 안에 추가된 메소드
// 체인 교체 코드
replaceChain(receivedChain: Block[]): Failable<undefined, string> {
const latestReceivedBlock: Block = receivedChain[receivedChain.length - 1];
const latestBlock: Block = this.getLatestBlock();
if (latestReceivedBlock.height === 0) {
return { isError: true, error: "받은 체인의 최신 블록이 제네시스 블록입니다." };
}
if (latestReceivedBlock.height <= latestBlock.height) {
return { isError: true, error: "자신의 체인이 더 길거나 같습니다." };
}
// 체인 바꿔주는 코드 (내 체인이 더 짧을 경우)
this.blockchain = receivedChain;
return { isError: false, value: undefined };
}
replaceChain( ) 메소드에서 검증하는 내용들은 다음과 같다.
- 받은체인의 최신블록.height === 0 (상대방이 제네시스 블록만 가지고 있음) → return
- 받은체인의 최신블록.height <= 내체인의 최신블록.height → return
replaceChain( ) 메소드 안에서 if 문을 통해 위의 내용들을 검증하게 되고 그 중 하나라도 해당된다면 { isError: true, error: " " } 를 return하면서 함수를 종료시킨다. 검증에 통과하지 못해 함수가 종료되었다면 A 노드의 블록체인은 B 노드로부터 전달 받은 블록체인으로 교체되지 않는다. 만약 성공적으로 검증이 완료되었다면 this.blockchain = receivedChain 에 의해 A 노드의 블록체인은 B 노드의 블록체인으로 교체된다.
다시 handleChainResponse( ) 메소드로 돌아가서 replaceChain( ) 함수 호출 이후의 코드를 살펴보면 broadcast와 관련된 코드들이 작성되어 있는 것을 확인할 수 있다. A 노드의 블록체인이 교체되었다면 broadcast 관련 코드들이 실행된다.
// broadcast 관련 코드
const message: Message = {
type: MessageType.receivedChain,
payload: receivedChain,
};
this.broadcast(message);
payload 속성 값이 receivedChain 이고 type 속성값이 MessageType.receivedChain 인 message 객체를 생성한 다음 this.broadcast( ) 메소드의 인자값으로 전달하였다. this.broadcast( ) 메소드는 P2PServer 클래스 안에서 새롭게 정의해준 메소드이며 해당 내용은 다음과 같다.
// P2PServer 클래스에 추가된 메소드
broadcast(message: Message): void {
this.sockets.forEach((socket) => P2PServer.send(socket)(message));
}
broadcast가 필요한 이유에 대해 잠시 짚고 넘어가자면, A 노드의 블록체인이 B 노드의 블록체인으로 교체되었을 때 A 노드는 자신과 연결되어 있는 모든 노드들에게 자신의 블록체인이 업데이트 되었다는 사실을 알려줘야 하기 때문이다. P2PServer 클래스의 속성값으로 만들어 놓았던 sockets 배열 안에는 A 노드와 연결되어 있는 소켓들이 들어있다. 따라서 sockets 배열 안에 있는 모든 socket들에게 P2PServer.send( socket )( message ) 메소드를 이용해 message 객체를 전송할 수 있다.
A 노드에서 broadcast를 했기 때문에 A 노드와 연결되어 있는 모든 노드들에서 messageHandler( ) 메소드 안에 있는 case MessageType.receivedChain: { } 코드블록이 실행된다. 이후의 과정은 앞서 설명한 내용과 동일하다.
이제 마지막으로 웹 소켓 연결이 끊겼을 때 어떻게 처리해 주는가에 대한 부분만 작업해주도록 하자.
P2PServer 클래스 안에 errorHandler( ) 메소드를 다음과 같이 만들어주었다.
errorHandler(socket: WebSocket) {
const close = () => {
this.sockets.splice(this.sockets.indexOf(socket), 1);
};
// socket 끊겼을 때
socket.on("close", close);
// error 발생시
socket.on("error", close);
}
errorHandler( ) 메소드 안의 socket.on( "close", close ) 와 socket.on( "error", close ) 에 의해 웹 소켓이 끊겼을 때 혹은 에러가 발생했을 때 close 함수가 실행된다. close 함수에서는 sockets 배열 안에 있는 socket들 중에서 연결이 끊긴 socket을 제거해주게 된다. 그리고 서버 쪽에서든 , 클라이언트 쪽에서든 연결이 되었을 때 항상 실행되는 connectSocket( ) 함수 안에서 errorHandler( ) 메소드를 호출시켜 놓았다.
connectSocket(socket: WebSocket) {
// 향후 broadcasting을 하기 위한 용도
this.sockets.push(socket);
this.messageHandler(socket);
const data: Message = {
type: MessageType.latest_block,
payload: {},
};
// 추가
this.errorHandler(socket);
const send = P2PServer.send(socket);
send(data);
}
이로써 블록체인 P2P 네트워크 구축 코드가 전부 완성되었다. 지금까지의 설명은 서버 쪽 역할을 하는 B 노드가 A 노드에게 데이터를 전송하는 것을 시작점으로 해서 어떠한 흐름으로 코드가 실행되는지에 관한 내용이었다. 잊지말아야 할 것은 지금까지의 모든 코드들은 서버 쪽 역할을 하는 B 노드 뿐만 아니라 클라이언트 쪽 역할을 하는 A 노드에서도 동일하게 실행된다는 사실이다. A 노드에서도 B 노드와 소켓 연결이 완료되었을 때 socket.on( "open" , ( ) => { } ) 에 의해 connectSocket( ) 함수가 호출되기 때문이다. A 노드의 connectSocket( ) 함수 안에 있는 send( data ) 로 인해 A 노드 역시 B 노드에게 데이터를 전송하게 되고 이 지점이 또 하나의 시작점이 되어 동일하지만 다른 방향으로 진행되는 코드의 흐름을 만들어낸다. 하지만 중요한 것은 결국 마지막엔 연결되어 있는 모든 노드들이 동일한 블록체인을 가지게 된다는 것이다. 그리고 연결되어 있는 모든 노드들이 동일한 상태의 블록체인을 가지게끔 계속해서 노드 간에 데이터 전송이 이루어지는 것,, 이것이 블록체인 P2P 네트워크의 핵심이 아닐까 싶다.
👉 p2p.ts 파일 (최종)
// p2p.ts 파일 (최종)
import { WebSocket } from "ws";
import { Chain } from "@core/blockchain/chain";
enum MessageType {
latest_block = 0,
all_block = 1,
receivedChain = 2,
}
interface Message {
type: MessageType;
payload: any;
}
export class P2PServer extends Chain {
private sockets: WebSocket[];
constructor() {
super();
this.sockets = [];
}
/**
* listen()
* 서버 입장
* 클라이언트가 연결을 시도했을 때 실행되는 코드
*/
listen() {
const server = new WebSocket.Server({ port: 7545 });
// 서버 기준 "connection"
server.on("connection", (socket) => {
console.log("webSocket connected");
this.connectSocket(socket);
});
}
/**
* connectToPeer()
* 클라이언트 입장
* 서버쪽으로 연결 요청시 실행되는 코드
*/
connectToPeer(newPeer: string) {
const socket = new WebSocket(newPeer);
// 클라이언트 기준 "open"
socket.on("open", () => {
this.connectSocket(socket);
});
}
connectSocket(socket: WebSocket) {
// 향후 broadcasting을 하기 위한 용도
this.sockets.push(socket);
this.messageHandler(socket);
const data: Message = {
type: MessageType.latest_block,
payload: {},
};
this.errorHandler(socket);
const send = P2PServer.send(socket);
send(data);
}
messageHandler(socket: WebSocket) {
const callback = (_data: string) => {
const result: Message = P2PServer.dataParse<Message>(_data);
const send = P2PServer.send(socket);
switch (result.type) {
// type에 따라 다른 처리를 해준다.
case MessageType.latest_block: {
const message: Message = {
type: MessageType.all_block,
payload: [this.getLatestBlock()],
};
send(message);
break;
}
case MessageType.all_block: {
const message: Message = {
type: MessageType.receivedChain,
payload: this.getChain(),
};
// ToDo : 체인에 블록을 추가할지 말지 결정
const [receivedBlock] = result.payload; // [this.getLatestBlock()]
const isValid = this.addToChain(receivedBlock);
// addToChain이 성공했을 때는 추가적인 요청이 불필요. -> break
if (!isValid.isError) break;
send(message);
break;
}
case MessageType.receivedChain: {
const receivedChain: IBlock[] = result.payload;
// ToDo : 체인을 교체하는 코드 (보다 긴 체인 선택하기)
this.handleChainResponse(receivedChain);
break;
}
}
};
socket.on("message", callback);
}
errorHandler(socket: WebSocket) {
const close = () => {
this.sockets.splice(this.sockets.indexOf(socket), 1);
};
// socket 끊겼을 때
socket.on("close", close);
// error 발생시
socket.on("error", close);
}
handleChainResponse(
receivedChain: IBlock[]
): Failable<Message | undefined, string> {
const isValidChain = this.isValidChain(receivedChain);
if (isValidChain.isError)
return { isError: true, error: isValidChain.error };
const isValid = this.replaceChain(receivedChain);
if (isValid.isError) return { isError: true, error: isValid.error };
// broadcast
const message: Message = {
type: MessageType.receivedChain,
payload: receivedChain,
};
this.broadcast(message);
return { isError: false, value: undefined };
}
broadcast(message: Message): void {
this.sockets.forEach((socket) => P2PServer.send(socket)(message));
}
static send(_socket: WebSocket) {
return (_data: Message) => {
_socket.send(JSON.stringify(_data));
};
}
static dataParse<T>(_data: string): T {
return JSON.parse(Buffer.from(_data).toString());
}
}
👉 chain.ts 파일 (최종)
// chian.ts 파일 (최종)
import { Block } from "@core/blockchain/block";
import { DIFFICULTY_ADJUSTMENT_INTERVAL } from "@core/config";
export class Chain {
private blockchain: Block[];
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];
}
public addBlock(data: string[]): Failable<Block, string> {
const previousBlock = this.getLatestBlock();
// -10 번째 블록 구하기
const adjustmentBlock: Block = this.getAdjustmentBlock();
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 };
}
public 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> {
// ToDo : 제네시스 블록을 검사하는 코드
// const genesis = _chain[0]
// ToDo : 나머지 체인에 대한 검증 코드
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> {
const latestReceivedBlock: Block = receivedChain[receivedChain.length - 1];
const latestBlock: Block = this.getLatestBlock();
if (latestReceivedBlock.height === 0) {
return { isError: true, error: "받은 체인의 최신 블록이 제네시스 블록입니다." };
}
if (latestReceivedBlock.height <= latestBlock.height) {
return { isError: true, error: "자신의 체인이 더 길거나 같습니다." };
}
// 체인 바꿔주는 코드 (내 체인이 더 짧다면)
this.blockchain = receivedChain;
return { isError: false, value: undefined };
}
/**
* getAdjustmentBlock()
* 생성 시점 기준으로 블록 높이가 -10 인 블록 구하기
* 1. 현재 높이값 < DIFFICULTY_ADJUSTMENT_INTERVAL : 제네시스 블록 반환
* 2. 현재 높이값 > DIFFICULTY_ADJUSTMENT_INTERVAL : -10번째 블록 반환
*/
public getAdjustmentBlock() {
const currentLength = this.getLength();
const adjustmentBlock: Block =
this.getLength() < DIFFICULTY_ADJUSTMENT_INTERVAL
? Block.getGENESIS()
: this.blockchain[currentLength - DIFFICULTY_ADJUSTMENT_INTERVAL];
return adjustmentBlock; // 제네시스 블록 or -10번째 블록 반환
}
}
👉 나머지 파일들은,,,
이전 게시글 참고)
2022.06.14 - [BlockChain] - BlockChain - 블록체인 P2P 네트워크 만들기 (1)
2022.06.15 - [분류 전체보기] - BlockChain - 블록체인 P2P 네트워크 만들기 (2)
'BlockChain' 카테고리의 다른 글
BlockChain - 블록체인 지갑 서버 만들기 (1) (0) | 2022.06.20 |
---|---|
BlockChain - 개인키, 공개키, 서명, 지갑/계정 (1) | 2022.06.17 |
BlockChain - 블록체인 P2P 네트워크 만들기 (2) (0) | 2022.06.15 |
BlockChain - 블록체인 P2P 네트워크 만들기 (1) (1) | 2022.06.14 |
BlockChain - TypeScript로 블록체인 만들기 (4) PoW (0) | 2022.06.14 |