본문으로 건너뛰기
Change page

네트워킹 계층

이더리움은 표준화된 프로토콜을 사용하여 서로 통신할 수 있어야 하는 수천 개의 노드로 구성된 피어 투 피어 네트워크입니다. "네트워킹 계층"은 이러한 노드들이 서로를 찾고 정보를 교환할 수 있게 해주는 프로토콜 스택입니다. 여기에는 네트워크를 통해 정보를 "가십(gossiping)"(일대다 통신)하는 것뿐만 아니라 특정 노드 간에 요청과 응답을 교환하는 것(일대일 통신)도 포함됩니다. 각 노드는 올바른 정보를 보내고 받을 수 있도록 특정 네트워킹 규칙을 준수해야 합니다.

클라이언트 소프트웨어에는 두 가지 부분(실행 클라이언트와 합의 클라이언트)이 있으며, 각각 고유한 네트워킹 스택을 가지고 있습니다. 실행 클라이언트와 합의 클라이언트는 다른 이더리움 노드와 통신할 뿐만 아니라 서로 간에도 통신해야 합니다. 이 페이지에서는 이러한 통신을 가능하게 하는 프로토콜에 대한 입문적인 설명을 제공합니다.

실행 클라이언트는 실행 계층 피어 투 피어 네트워크를 통해 트랜잭션을 가십합니다. 이를 위해서는 인증된 피어 간의 암호화된 통신이 필요합니다. 검증자가 블록을 제안하도록 선택되면, 노드의 로컬 트랜잭션 풀에 있는 트랜잭션이 로컬 RPC 연결을 통해 합의 클라이언트로 전달되며, 이는 비콘 블록으로 패키징됩니다. 그런 다음 합의 클라이언트는 p2p 네트워크를 통해 비콘 블록을 가십합니다. 이를 위해서는 두 개의 별도 p2p 네트워크가 필요합니다. 하나는 트랜잭션 가십을 위해 실행 클라이언트를 연결하고, 다른 하나는 블록 가십을 위해 합의 클라이언트를 연결합니다.

전제 조건

이 페이지를 이해하려면 이더리움 노드 및 클라이언트에 대한 약간의 지식이 도움이 될 것입니다.

실행 계층

실행 계층의 네트워킹 프로토콜은 두 가지 스택으로 나뉩니다.

  • 디스커버리 스택: UDP 위에 구축되며 새로운 노드가 연결할 피어를 찾을 수 있게 해줍니다.

  • DevP2P 스택: TCP 위에 위치하며 노드들이 정보를 교환할 수 있게 해줍니다.

두 스택은 병렬로 작동합니다. 디스커버리 스택은 새로운 네트워크 참여자를 네트워크로 유입시키고, DevP2P 스택은 이들의 상호 작용을 가능하게 합니다.

디스커버리

디스커버리는 네트워크에서 다른 노드를 찾는 과정입니다. 이는 소수의 부트노드(클라이언트에 주소가 하드코딩 (opens in a new tab)되어 있어 즉시 찾을 수 있고 클라이언트를 피어에 연결할 수 있는 노드)를 사용하여 부트스트랩됩니다. 이러한 부트노드는 새로운 노드를 피어 그룹에 소개하기 위해서만 존재합니다. 이것이 유일한 목적이며, 체인 동기화와 같은 일반적인 클라이언트 작업에는 참여하지 않고 클라이언트가 처음 실행될 때만 사용됩니다.

노드와 부트노드 간의 상호 작용에 사용되는 프로토콜은 노드 목록을 공유하기 위해 분산 해시 테이블 (opens in a new tab)을 사용하는 수정된 형태의 Kademlia (opens in a new tab)입니다. 각 노드는 가장 가까운 피어에 연결하는 데 필요한 정보가 포함된 이 테이블의 버전을 가지고 있습니다. 이 '가까움'은 지리적인 것이 아닙니다. 거리는 노드 ID의 유사성에 의해 정의됩니다. 각 노드의 테이블은 보안 기능으로서 정기적으로 새로 고쳐집니다. 예를 들어, Discv5 (opens in a new tab)에서 디스커버리 프로토콜 노드는 클라이언트가 지원하는 하위 프로토콜을 표시하는 '광고(ads)'를 보낼 수도 있어, 피어들이 통신에 사용할 수 있는 프로토콜에 대해 협상할 수 있게 합니다.

디스커버리는 PING-PONG 게임으로 시작됩니다. 성공적인 PING-PONG은 새로운 노드를 부트노드에 "결속(bond)"시킵니다. 네트워크에 진입하는 새로운 노드의 존재를 부트노드에 알리는 초기 메시지는 PING입니다. 이 PING에는 새로운 노드, 부트노드 및 만료 타임스탬프에 대한 해시된 정보가 포함됩니다. 부트노드는 PING를 수신하고 PING 해시가 포함된 PONG을 반환합니다. PINGPONG 해시가 일치하면 새로운 노드와 부트노드 간의 연결이 확인되며, 이들이 "결속되었다"고 합니다.

결속되면 새로운 노드는 부트노드에 FIND-NEIGHBOURS 요청을 보낼 수 있습니다. 부트노드가 반환하는 데이터에는 새로운 노드가 연결할 수 있는 피어 목록이 포함됩니다. 노드들이 결속되지 않은 경우 FIND-NEIGHBOURS 요청이 실패하므로 새로운 노드는 네트워크에 진입할 수 없습니다.

새로운 노드가 부트노드로부터 이웃 목록을 받으면, 각 이웃과 PING-PONG 교환을 시작합니다. 성공적인 PING-PONG은 새로운 노드를 이웃과 결속시켜 메시지 교환을 가능하게 합니다.

클라이언트 시작 --> 부트노드 연결 --> 부트노드 결속 --> 이웃 찾기 --> 이웃 결속

실행 클라이언트는 현재 Discv4 (opens in a new tab) 디스커버리 프로토콜을 사용하고 있으며, Discv5 (opens in a new tab) 프로토콜로 마이그레이션하기 위한 활발한 노력이 진행 중입니다.

ENR: 이더리움 노드 레코드

이더리움 노드 레코드(ENR)는 세 가지 기본 요소를 포함하는 객체입니다. 서명(합의된 신원 체계에 따라 만들어진 레코드 내용의 해시), 레코드의 변경 사항을 추적하는 시퀀스 번호, 그리고 임의의 키:값 쌍 목록입니다. 이는 새로운 피어 간에 식별 정보를 더 쉽게 교환할 수 있게 해주는 미래 지향적인 형식이며, 이더리움 노드에서 선호하는 네트워크 주소 형식입니다.

디스커버리가 UDP 위에 구축된 이유는 무엇인가요?

UDP는 오류 검사, 실패한 패킷의 재전송, 또는 동적으로 연결을 열고 닫는 것을 지원하지 않습니다. 대신 성공적으로 수신되었는지 여부와 관계없이 대상에 정보의 연속적인 스트림을 쏘기만 합니다. 이러한 최소한의 기능은 최소한의 오버헤드로 이어져 이러한 종류의 연결을 매우 빠르게 만듭니다. 노드가 피어와 공식적인 연결을 설정하기 위해 단순히 자신의 존재를 알리고자 하는 디스커버리의 경우, UDP로 충분합니다. 그러나 네트워킹 스택의 나머지 부분에는 UDP가 목적에 적합하지 않습니다. 노드 간의 정보 교환은 상당히 복잡하므로 재전송, 오류 검사 등을 지원할 수 있는 더 완전한 기능을 갖춘 프로토콜이 필요합니다. TCP와 관련된 추가 오버헤드는 추가 기능만큼의 가치가 있습니다. 따라서 P2P 스택의 대부분은 TCP를 통해 작동합니다.

DevP2P

DevP2P는 이더리움이 피어 투 피어 네트워크를 설정하고 유지하기 위해 구현하는 전체 프로토콜 스택입니다. 새로운 노드가 네트워크에 진입한 후, 이들의 상호 작용은 DevP2P (opens in a new tab) 스택의 프로토콜에 의해 관리됩니다. 이들은 모두 TCP 위에 위치하며 RLPx 전송 프로토콜, 와이어 프로토콜 및 여러 하위 프로토콜을 포함합니다. RLPx (opens in a new tab)는 노드 간 세션의 시작, 인증 및 유지를 관리하는 프로토콜입니다. RLPx는 노드 간 전송을 위해 데이터를 최소한의 구조로 인코딩하는 매우 공간 효율적인 방법인 RLP(Recursive Length Prefix)를 사용하여 메시지를 인코딩합니다.

두 노드 간의 RLPx 세션은 초기 암호화 핸드셰이크로 시작됩니다. 여기에는 노드가 인증 메시지를 보내고 피어가 이를 검증하는 과정이 포함됩니다. 검증에 성공하면 피어는 인증 확인 메시지를 생성하여 시작 노드에 반환합니다. 이는 노드들이 비공개로 안전하게 통신할 수 있게 해주는 키 교환 과정입니다. 성공적인 암호화 핸드셰이크는 두 노드가 "와이어 상에서(on the wire)" 서로에게 "hello" 메시지를 보내도록 트리거합니다. 와이어 프로토콜은 hello 메시지의 성공적인 교환에 의해 시작됩니다.

hello 메시지에는 다음이 포함됩니다.

  • 프로토콜 버전
  • 클라이언트 ID
  • 포트
  • 노드 ID
  • 지원되는 하위 프로토콜 목록

이는 두 노드 간에 공유되는 기능이 무엇인지 정의하고 통신을 구성하기 때문에 성공적인 상호 작용에 필요한 정보입니다. 각 노드가 지원하는 하위 프로토콜 목록을 비교하여 두 노드에 공통적인 프로토콜을 세션에서 사용할 수 있도록 하는 하위 프로토콜 협상 과정이 있습니다.

hello 메시지와 함께, 와이어 프로토콜은 연결이 닫힐 것임을 피어에게 경고하는 "disconnect" 메시지를 보낼 수도 있습니다. 와이어 프로토콜에는 세션을 열어두기 위해 주기적으로 전송되는 PING 및 PONG 메시지도 포함됩니다. 따라서 RLPx 및 와이어 프로토콜 교환은 노드 간 통신의 기반을 설정하여 특정 하위 프로토콜에 따라 유용한 정보가 교환될 수 있는 토대를 제공합니다.

하위 프로토콜

와이어 프로토콜

피어가 연결되고 RLPx 세션이 시작되면 와이어 프로토콜이 피어 간의 통신 방법을 정의합니다. 초기에 와이어 프로토콜은 체인 동기화, 블록 전파 및 트랜잭션 교환이라는 세 가지 주요 작업을 정의했습니다. 그러나 이더리움이 지분 증명(PoS)으로 전환되면서 블록 전파와 체인 동기화는 합의 레이어의 일부가 되었습니다. 트랜잭션 교환은 여전히 실행 클라이언트의 소관입니다. 트랜잭션 교환은 블록 빌더가 다음 블록에 포함할 트랜잭션을 선택할 수 있도록 노드 간에 대기 중인 트랜잭션을 교환하는 것을 의미합니다. 이러한 작업에 대한 자세한 정보는 여기 (opens in a new tab)에서 확인할 수 있습니다. 이러한 하위 프로토콜을 지원하는 클라이언트는 JSON-RPC를 통해 이를 노출합니다.

les (경량 이더리움 하위 프로토콜)

이는 경량 클라이언트를 동기화하기 위한 최소한의 프로토콜입니다. 전통적으로 이 프로토콜은 풀 노드가 인센티브 없이 경량 클라이언트에 데이터를 제공해야 하기 때문에 거의 사용되지 않았습니다. 실행 클라이언트의 기본 동작은 les를 통해 경량 클라이언트 데이터를 제공하지 않는 것입니다. 자세한 정보는 les 사양 (opens in a new tab)에서 확인할 수 있습니다.

Snap

snap 프로토콜 (opens in a new tab)은 피어가 최근 상태의 스냅샷을 교환할 수 있게 해주는 선택적 확장으로, 피어가 중간 머클 트라이 노드를 다운로드하지 않고도 계정 및 스토리지 데이터를 검증할 수 있게 합니다.

Wit (위트니스 프로토콜)

위트니스 프로토콜 (opens in a new tab)은 피어 간에 상태 증거(위트니스)를 교환할 수 있게 해주는 선택적 확장으로, 클라이언트를 체인의 끝(tip)에 동기화하는 데 도움을 줍니다.

Whisper

Whisper는 블록체인에 어떠한 정보도 기록하지 않고 피어 간에 안전한 메시징을 제공하는 것을 목표로 한 프로토콜이었습니다. 이는 DevP2P 와이어 프로토콜의 일부였으나 현재는 더 이상 사용되지 않습니다. 비슷한 목표를 가진 다른 관련 프로젝트 (opens in a new tab)들이 존재합니다.

합의 레이어

합의 클라이언트는 다른 사양을 가진 별도의 피어 투 피어 네트워크에 참여합니다. 합의 클라이언트는 피어로부터 새로운 블록을 수신하고 자신이 블록 제안자가 될 차례일 때 이를 브로드캐스트할 수 있도록 블록 가십에 참여해야 합니다. 실행 계층과 유사하게, 이를 위해서는 먼저 노드가 피어를 찾고 블록, 증명 등을 교환하기 위한 안전한 세션을 설정할 수 있도록 디스커버리 프로토콜이 필요합니다.

디스커버리

실행 클라이언트와 유사하게, 합의 클라이언트는 피어를 찾기 위해 UDP를 통한 discv5 (opens in a new tab)를 사용합니다. 합의 레이어의 discv5 구현은 discv5를 libp2p (opens in a new tab) 스택에 연결하는 어댑터를 포함하여 DevP2P를 더 이상 사용하지 않는다는 점에서만 실행 클라이언트의 구현과 다릅니다. 실행 계층의 RLPx 세션은 libp2p의 노이즈 보안 채널 핸드셰이크를 위해 더 이상 사용되지 않습니다.

ENR

합의 노드의 ENR에는 노드의 공개키, IP 주소, UDP 및 TCP 포트와 두 가지 합의 전용 필드인 증명 서브넷 비트필드 및 eth2 키가 포함됩니다. 전자는 노드가 특정 증명 가십 하위 네트워크에 참여하는 피어를 더 쉽게 찾을 수 있게 해줍니다. eth2 키에는 노드가 사용 중인 이더리움 포크 버전에 대한 정보가 포함되어 있어 피어가 올바른 이더리움에 연결되도록 보장합니다.

libp2p

libp2p 스택은 디스커버리 이후의 모든 통신을 지원합니다. 클라이언트는 ENR에 정의된 대로 IPv4 및/또는 IPv6에서 다이얼하고 수신 대기할 수 있습니다. libp2p 레이어의 프로토콜은 가십 및 요청/응답(req/resp) 도메인으로 세분화될 수 있습니다.

가십

가십 도메인에는 네트워크 전체에 빠르게 퍼져야 하는 모든 정보가 포함됩니다. 여기에는 비콘 블록, 증명(proofs), 증명(attestations), 종료(exits) 및 슬래싱이 포함됩니다. 이는 libp2p gossipsub v1을 사용하여 전송되며, 수신 및 전송할 가십 페이로드의 최대 크기를 포함하여 각 노드에 로컬로 저장된 다양한 메타데이터에 의존합니다. 가십 도메인에 대한 자세한 정보는 여기 (opens in a new tab)에서 확인할 수 있습니다.

요청-응답

요청-응답 도메인에는 클라이언트가 피어에게 특정 정보를 요청하기 위한 프로토콜이 포함되어 있습니다. 예를 들어 특정 루트 해시와 일치하거나 슬롯 범위 내에 있는 특정 비콘 블록을 요청하는 것이 포함됩니다. 응답은 항상 snappy로 압축된 SSZ 인코딩 바이트로 반환됩니다.

합의 클라이언트가 RLP보다 SSZ를 선호하는 이유는 무엇인가요?

SSZ는 단순 직렬화(simple serialization)를 의미합니다. 이는 고정된 오프셋을 사용하여 전체 구조를 디코딩할 필요 없이 인코딩된 메시지의 개별 부분을 쉽게 디코딩할 수 있게 해줍니다. 이는 인코딩된 메시지에서 특정 정보 조각을 효율적으로 가져올 수 있으므로 합의 클라이언트에 매우 유용합니다. 또한 머클 프로토콜과 통합되도록 특별히 설계되어 머클화(Merkleization)에 대한 관련 효율성 향상을 제공합니다. 합의 레이어의 모든 해시는 머클 루트이므로 이는 상당한 개선으로 이어집니다. SSZ는 또한 값의 고유한 표현을 보장합니다.

실행 클라이언트와 합의 클라이언트 연결

합의 클라이언트와 실행 클라이언트는 모두 병렬로 실행됩니다. 합의 클라이언트가 실행 클라이언트에 지침을 제공하고, 실행 클라이언트가 비콘 블록에 포함할 트랜잭션 번들을 합의 클라이언트에 전달할 수 있도록 이들은 연결되어야 합니다. 두 클라이언트 간의 통신은 로컬 RPC 연결을 사용하여 달성할 수 있습니다. 'Engine-API' (opens in a new tab)로 알려진 API는 두 클라이언트 간에 전송되는 지침을 정의합니다. 두 클라이언트 모두 단일 네트워크 신원 뒤에 위치하므로, 각 클라이언트에 대한 별도의 키(이더1 키 및 이더2 키)가 포함된 ENR(이더리움 노드 레코드)을 공유합니다.

제어 흐름의 요약은 아래에 나와 있으며, 관련 네트워킹 스택은 괄호 안에 표시되어 있습니다.

합의 클라이언트가 블록 생성자가 아닌 경우:

  • 합의 클라이언트가 블록 가십 프로토콜을 통해 블록을 수신합니다(합의 p2p).
  • 합의 클라이언트가 블록을 사전 검증합니다. 즉, 올바른 메타데이터를 가진 유효한 발신자로부터 도착했는지 확인합니다.
  • 블록의 트랜잭션은 실행 페이로드로서 실행 계층으로 전송됩니다(로컬 RPC 연결).
  • 실행 계층은 트랜잭션을 실행하고 블록 헤더의 상태를 검증합니다(즉, 해시가 일치하는지 확인).
  • 실행 계층은 검증 데이터를 합의 레이어로 다시 전달하며, 이제 블록은 검증된 것으로 간주됩니다(로컬 RPC 연결).
  • 합의 레이어는 자체 블록체인의 헤드에 블록을 추가하고 이를 증명하며, 네트워크를 통해 증명을 브로드캐스트합니다(합의 p2p).

합의 클라이언트가 블록 생성자인 경우:

  • 합의 클라이언트가 다음 블록 생성자라는 알림을 받습니다(합의 p2p).
  • 합의 레이어가 실행 클라이언트에서 create block 메서드를 호출합니다(로컬 RPC).
  • 실행 계층은 트랜잭션 가십 프로토콜에 의해 채워진 트랜잭션 멤풀에 액세스합니다(실행 p2p).
  • 실행 클라이언트는 트랜잭션을 블록으로 묶고, 트랜잭션을 실행하며, 블록 해시를 생성합니다.
  • 합의 클라이언트는 실행 클라이언트에서 트랜잭션과 블록 해시를 가져와 비콘 블록에 추가합니다(로컬 RPC).
  • 합의 클라이언트는 블록 가십 프로토콜을 통해 블록을 브로드캐스트합니다(합의 p2p).
  • 다른 클라이언트는 블록 가십 프로토콜을 통해 제안된 블록을 수신하고 위에서 설명한 대로 검증합니다(합의 p2p).

블록이 충분한 검증자에 의해 증명되면 체인의 헤드에 추가되고, 정당화된 후 최종적으로 완결된 상태가 됩니다.

Diagram of the Ethereum consensus client networking layer Diagram of the Ethereum execution client networking layer

ethresear.ch (opens in a new tab)의 합의 및 실행 클라이언트를 위한 네트워크 계층 회로도

더 읽을거리

DevP2P (opens in a new tab) libp2p (opens in a new tab) 합의 레이어 네트워크 사양 (opens in a new tab) kademlia에서 discv5로 (opens in a new tab) kademlia 논문 (opens in a new tab) 이더리움 p2p 소개 (opens in a new tab) 이더1/이더2 관계 (opens in a new tab) 병합 및 이더2 클라이언트 세부 정보 비디오 (opens in a new tab)