P2P 통신을 통해 노드 간 체인 주고받기
import { WebSocket } from 'ws';
//chain
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[];
// public blockchain : Chain
constructor() {
super();
// this.blockchain = new Chain()
this.sockets = [];
}
getSockets() {
return this.sockets;
}
// 서버 시작 실행 코드
listen() {
const server = new WebSocket.Server({ port: 7545 });
server.on('connection', (socket) => {
console.log('websocket connection');
this.connectSocket(socket);
// websocket이 외와 연결될 경우 코드 실행, 콘솔 찍고 sockets 배열에 푸시
// 나 자신은 제외다. 상대방에 대해서만 실행됨
});
// on은 이벤트 리스너라고 보면 될 것 같다.
// 커넥션이 된다? > 콜백을 실행
}
// 클라이언트가 나에게 연결을 시도했을 때만 실행되는 코드
// 클라이언트 연결 코드
connectToPeer(newPeer: string) {
const socket = new WebSocket(newPeer);
// 원하는 ip로 요청을 보낼 수 있게..
socket.on('open', () => {
this.connectSocket(socket);
});
}
// 상대방이 내게 연결할때
//
connectSocket(socket: WebSocket) {
this.sockets.push(socket);
// 연결된 socket을 배열에 추가
this.messageHandler(socket);
// 연결된 소켓에 대해 메시지 수신에 대한 이벤트 등록
// 연결된 소켓이 내게 보낸 메시지..
const data: Message = {
type: MessageType.latest_block,
payload: {},
};
// socket.send('bitcoin is ponzi')
// 나와 연결된 클라리언트에 내용을 전송
// 트리거없이 그냥..
// 얘가 위에 oscket.on보다 먼저 실행되서 메시지 실행 후, 콜백 실행
// qwui를 매개변수로 받는다.
//socket.send(JSON.stringify(data))
// 상대에게 최신 블럭을 스트링으로 전송
this.errorHandler(socket);
this.send(socket)(data);
// 메시지 전송 > node2의 마지막 블럭 데이터을 받기 위해..
// node2는 요청 받은 데이터를 보내줌
}
messageHandler(socket: WebSocket) {
const callback = (data: string) => {
console.log(data); // buffer
// console.log(Buffer.from(data).toString()) // buffer > string
// const Block : Message = P2PServer.dataParse<Message>(data)
const result: Message = P2PServer.dataParse<Message>(data);
const send = this.send(socket);
switch (result.type) {
case MessageType.latest_block: {
const message: Message = {
type: MessageType.all_block,
payload: [this.getLatestBlock()],
};
send(message);
break;
}
// 내가 서버일때 클라이언트로부터 요청을 받음
// > 클라이언트에게 응답 (send message)
// 서버가 이 코드가 실행되면 클라이언트는 아래 코드가 실행됨
// 이 코드의 message = 아래의 result.payload
case MessageType.all_block: {
const message: Message = {
type: MessageType.receivedChain,
payload: this.getChain(),
};
// 블럭 검증 코드 실행 이후, 블럭을 체인에 넣을지 말지 결정
// 내가 가진 체인의 최신 블럭, 받은 블럭을 비교
// 내 hash와 상대방 previousHash가 같다면 길이 차이는 1, 상대방 체인 길이가 1 길다.
// 이 경우, 내 체인에 상대방 블럭을 추가
const [receivedBlock] = result.payload; // this.getLatestBlock()
const isValid = this.addToChain(receivedBlock);
console.log(isValid);
if (!isValid.isError) break;
// chain에 블럭이 추가되었다면 메시지를 따로 보내지 않고 종료
send(message);
break;
}
//
case MessageType.receivedChain: {
const receivedChain: IBlock[] = result.payload;
//체인 바꿔주는 코드
//긴 체인 선택하기
this.handleChainResponse(receivedChain);
break;
}
// 내가 클라이언트일 때 서버에서 전체 블럭을 달라고 요청하는 코드
}
};
socket.on('message', callback);
}
errorHandler(socket: WebSocket) {
const close = () => {
this.sockets.splice(this.sockets.indexOf(socket), 1);
};
socket.on('close', close);
socket.on('error', close);
}
//
send(_socket: WebSocket) {
return (_data: Message) => {
_socket.send(JSON.stringify(_data));
};
}
broadCast(message: Message): void {
this.sockets.forEach((socket) => this.send(socket)(message));
}
handleChainResponse(receivedChain: IBlock[]): Failable<Message | undefined, string> {
//체인 바꾸기
const isValidChain = this.isValidChain(receivedChain);
//전달받은 체인이 올바른 체인인 지 검증
if (isValidChain.isError) return { isError: true, error: isValidChain.error };
//내 체인과 상대방 체인에 대해서 검사해서 내 체인을 변화시킬 이유가 없을 경우 함수 return
//1. 받은 체인의 최신 블럭 height와 내 체인의 최신블록 height를 비교해서, 내 height가 더 길면 내가 더 최신블록이므로 return
//1. 받은 최신블록.height <= 내 체인 최신블록.height이면 return
//2. 받은 최신블럭의 previousHash 값 === 내 체인의 hash값
//3. 받은 체인의 길이 === 1 .... 상대가 제네시스 밖에 없는 경우 return
//4. 여기까지왔다면 내 체인이 더 짧다....체인 바꾸는 코드 실행
const isValid = this.replaceChain(receivedChain);
if (isValid.isError) {
return { isError: true, error: isValid.error };
}
//여기까지 코드가 실행됬다는 건 받은 체인이 더 길다는 뜻이니까 나랑 연결된 모든 노드들에게 브로드 캐스트로 변경된(받은 체인)을 페이로드로 전달함
const message: Message = {
type: MessageType.receivedChain,
payload: receivedChain,
};
this.broadCast(message);
return {
isError: false,
value: undefined,
};
}
// 이 고차함수를 connectSocket에서 실행
// 소켓에서도 데이터 타입에따라 어떻게 처리를 할지 정해줘야한다.
static dataParse<T>(_data: string): T {
// const result = JSON.parse(Buffer.from(_data).toString())
// if( result === undefined ) return { isError : true, error : 'conversion failed'}
// return { isError : false, value : result }
return JSON.parse(Buffer.from(_data).toString());
}
}
'블록체인' 카테고리의 다른 글
이더리움 생태계 (0) | 2022.06.27 |
---|---|
transaction 생성 (0) | 2022.06.22 |
채굴(mining) (0) | 2022.06.20 |
P2P 통신으로 노드 간 블록 주고받기 (0) | 2022.06.14 |
이론1. 블록체인 개념 (0) | 2022.06.08 |
댓글