메인 콘텐츠로 건너뛰기
이 문서는 StableNet 노드 간 연결을 관리하는 피어투피어 네트워킹 레이어를 다룹니다. 특히 p2p.Server 아키텍처, 피어 라이프사이클 관리, 연결 타입, 프로토콜 멀티플렉싱에 대한 세부사항을 제공합니다.
피어 발견 메커니즘에 대한 정보는 발견 메커니즘을 참조하세요.
블록체인 동기화 프로토콜에 대한 세부사항은 체인 동기화을 참조하세요.
프로토콜 핸들러 구현은 프로토콜 핸들러을 참조하세요.

서버 아키텍처

P2P 레이어는 모든 피어 연결을 관리하고 StableNet 노드의 핵심 네트워킹 기능을 구현하는 p2p.Server 구조체를 중심으로 구성됩니다.
이 서버는 인바운드 및 아웃바운드 연결을 통합 관리하며, 발견 프로토콜과 프로토콜 핸들러 사이의 중재자 역할을 수행합니다.

서버 구조

Server 구조체는 다음과 같은 주요 구성 요소를 유지합니다:
ComponentTypePurpose
Configp2p.Config최대 피어 수, 리슨 주소, 활성 프로토콜 등 네트워크 구성
localnode*enode.LocalNode로컬 노드의 신원 정보 및 ENR 관리
listenernet.Listener인바운드 TCP 연결 수신
dialsched*dialScheduler아웃바운드 연결 시도 스케줄링
discmix*enode.FairMix여러 발견 소스를 집계
ntab*discover.UDPv4Discovery v4 프로토콜 핸들러
DiscV5*discover.UDPv5Discovery v5 프로토콜 핸들러
nodedb*enode.DB영속 노드 데이터베이스

서버 구성 및 초기화

서버 초기화 시퀀스는 다음과 같은 단계로 진행됩니다:
  1. setupLocalNode()
    개인 키를 기반으로 로컬 노드 ID와 ENR을 생성하고 노드 데이터베이스를 초기화합니다.
  2. setupListening()
    구성된 네트워크 주소에서 TCP 리스너를 시작합니다.
  3. setupDiscovery()
    Discovery v4/v5 프로토콜을 초기화하고 발견 믹서를 구성합니다.
  4. setupDialScheduler()
    정적 노드 및 발견 노드를 대상으로 하는 다이얼 스케줄러를 생성합니다.
  5. run()
    피어 이벤트를 처리하는 서버 메인 이벤트 루프를 시작합니다.
  6. listenLoop()
    인바운드 연결을 지속적으로 수락합니다.

서버 메인 루프

서버의 메인 이벤트 루프는 다음과 같은 역할을 수행합니다:
  • 피어 추가 및 제거 이벤트 처리
  • 다이얼 스케줄러로부터의 연결 요청 처리
  • 종료 신호 수신 시 모든 피어를 정상 종료
  • 이벤트 피드로 피어 상태 변경 알림 전파

피어 라이프사이클

각 연결된 피어는 연결 상태, 프로토콜 핸들러, 수명 주기 정보를 캡슐화하는 Peer 구조체로 표현됩니다.

연결 설정 흐름

피어 연결은 다음 흐름을 따릅니다:
  1. TCP 연결 수립
  2. 암호화 핸드셰이크 수행
  3. 프로토콜 핸드셰이크 및 기능 협상
  4. 프로토콜 멀티플렉싱 설정
  5. 피어 등록 및 이벤트 발생

핸드셰이크 프로토콜

연결 설정에는 두 단계의 핸드셰이크가 포함됩니다:
  1. 암호화 핸드셰이크 (doEncHandshake)
    RLPX 기반 암호화 채널을 설정합니다.
  2. 프로토콜 핸드셰이크 (doProtoHandshake)
    지원 프로토콜 및 버전을 협상합니다.
protoHandshake 구조체는 다음 정보를 포함합니다:
FieldTypeDescription
Versionuint64P2P 프로토콜 버전
Namestring클라이언트 식별 문자열
Caps[]Cap지원 프로토콜 기능 목록
ListenPortuint64리슨 포트
ID[]byte노드 공개 키

프로토콜 멀티플렉싱

핸드셰이크 이후 서버는 공통으로 지원되는 프로토콜을 매칭하고, 각 프로토콜에 메시지 코드 범위를 할당합니다.
  • 기본 P2P 메시지: 코드 0–15
  • 첫 번째 매칭된 서브프로토콜: 코드 16부터 시작
  • 이후 프로토콜은 이전 프로토콜 길이만큼 오프셋 증가
이 구조를 통해 단일 연결에서 여러 프로토콜 메시지를 안전하게 다룰 수 있습니다.

연결 타입 및 관리

P2P 서버는 connFlag를 사용하여 연결 타입을 구분합니다:
FlagValueDescription
dynDialedConn1 << 0발견을 통해 생성된 아웃바운드 연결
staticDialedConn1 << 1정적 노드로의 아웃바운드 연결
inboundConn1 << 2외부에서 들어온 인바운드 연결
trustedConn1 << 3신뢰 피어(MaxPeers 제한 초과 가능)

피어 제한 및 용량

기본적인 피어 용량 제어 정책은 다음과 같습니다:
  • MaxPeers: 전체 피어 수 제한
  • MaxPendingPeers: 연결 대기 피어 수 제한(방향당 기본 50)
  • DialRatio: 아웃바운드 피어 비율(기본 1/3)

다이얼 스케줄러

dialScheduler는 아웃바운드 연결 시도를 관리합니다:
  • 최근 다이얼 기록을 유지하여 과도한 재시도를 방지
  • 정적 노드를 발견 노드보다 우선 연결
  • 최대 다이얼 연결 수 제한 준수
  • 지수 백오프를 사용하여 실패한 엔드포인트 재시도 제한

피어 작업 및 라이프사이클

피어 추가 및 제거

서버는 다음 메서드를 통해 피어 집합을 관리합니다:
MethodPurpose
AddPeer(node)정적 피어 추가 및 자동 재연결
RemovePeer(node)정적 피어 제거 및 연결 해제
AddTrustedPeer(node)신뢰 피어 추가
RemoveTrustedPeer(node)신뢰 피어 제거

연결 해제 이유

연결 해제 사유는 DiscReason 코드로 기록됩니다:
ReasonCodeDescription
DiscRequested0x00정상 종료
DiscNetworkError0x01네트워크 오류
DiscProtocolError0x02프로토콜 위반
DiscUselessPeer0x03유효하지 않은 피어
DiscTooManyPeers0x04피어 수 제한 초과
DiscAlreadyConnected0x05중복 연결
DiscIncompatibleVersion0x06버전 불일치
DiscInvalidIdentity0x07잘못된 노드 ID
DiscQuitting0x08서버 종료
DiscSubprotocolError0x10서브프로토콜 오류

피어 이벤트 피드

서버는 피어 상태 변화를 PeerEvent로 외부에 알립니다:
Event TypeDescription
PeerEventTypeAdd피어 연결 및 등록
PeerEventTypeDrop피어 연결 해제
PeerEventTypeMsgSend메시지 전송
PeerEventTypeMsgRecv메시지 수신

Ethereum 백엔드와의 통합

Ethereum 백엔드는 프로토콜 등록 과정을 통해 P2P 서버와 통합됩니다. 통합 흐름은 다음과 같습니다:
  1. eth.Ethereum.Protocols() – 프로토콜 정의 반환
  2. eth.MakeProtocols() – eth 서브프로토콜 핸들러 생성
  3. snap.MakeProtocols() – snap 프로토콜 핸들러 생성(활성화 시)
  4. node.Stack.RegisterProtocols() – P2P 서버에 등록
  5. handler.Start() – 프로토콜 메시지 처리 시작

백엔드의 P2P 서버 참조

백엔드는 RPC 서비스를 위해 P2P 서버 참조를 유지하며, 이는 다음 용도로 사용됩니다:
  • NetAPI – 네트워크 정보 조회
  • AdminAPI – 피어 관리 RPC
  • ENR 동적 업데이트

구성 플래그

P2P 네트워크 구성에 사용되는 주요 플래그는 다음과 같습니다:
FlagTypeDefaultDescription
--maxpeersintDefaultConfig.P2P.MaxPeers최대 피어 수
--maxpendpeersint0대기 피어 수 제한
--portint30303네트워크 리슨 포트
--bootnodesstringNetwork-specific부트스트랩 노드
--nodekeystring-P2P 노드 키 경로
--nodekeyhexstring-테스트용 노드 키(hex)
--nodiscoverboolfalse피어 발견 비활성화
--netrestrictstring-CIDR 네트워크 제한
이 구성 값들은 노드 초기화 과정에서 SetP2PConfig()setNodeKey()를 통해 적용됩니다.