Ethereum(DEVp2p Protocol) 상의 다양한 Blockchain Network Property를 측정하고 수집함.
구동 대상(범위)와 방법?
약 3달 간 Ubuntu 16.04 머신 1개에 약 30개의 NodeFinder Instance 실행. 37개의 Measurement Nodes와 242개의 NodeFinder 실행 Node(?)를 돌려서 각종 Property를 수집한다.
해당 연구로 얻어 낸 결과값?
Longitudinal View: 노드 탐색 요청 수, 탐색 시도한 노드 수, 탐색에 응답한 노드 수, bootstrapping 노드와 연결된 static-dial 노드 수, Abusive Nodes의 존재 확인
Peer Eco-System View (Longitudinal View): DEVp2p Protocol을 사용하는 다양한 Network Peers의 존재 확인, 다양한 Client와 Version의 사용 현황, Geth-Parity 간 Implementation의 차이와 그로 인한 현상
Snapshot View: 네트워크 크기, 나라/AS별 네트워크 분포, Direct/Remote Peer 지연 시간, Client Age (Patch Version), Node Freshness
의의 (Novel Points)
Ethereum의 Underlying Network의 중요성을 조명하고, 이를 이용해 중요한 property를 뽑아내려는 시도
저장하고 싶은 Data의 Hash와 각 Node의 ID, 160bit value에 대해 XOR을 취해 distance를 계산한다. 가장 distance가 적은 node에 해당 Data를 저장한다.
Kademlia: Data의 탐색
각 Kademlia Node는 연결된 peer들을 모니터링하고 해당 데이터를 탐색하기 위한 Routing Table이 있다. Table은 160개의 Bucket으로 이루어져 있고, 각 Bucket은 최대 k개의 node entry를 저장한다. (k-bucket) Bucket을 나누는 기준은 (Routing Table을 보유한 Node ID) XOR (대상 Node ID)이며 2의 지수승 단위로 나눈다. (1~2, 2~3, 4~7, 8~15, 16~32, ...) Eviction Policy는 기존 entry를 최대한 지키는 방향이며, PING message에 PONG message를 respond하지 않는 상황과 같은 inactive한 상황이 아니라면 새로이 들어온 node를 evict한다.
Kademlia: Peer의 탐색
RLPx: RLPx vs Kademlia
RLPx는 Data 저장/복구를 지원하지 않는다. 오로지 Peer 탐색만 수행
RLPx의 Node ID는 160bit가 아닌 512bit
RLPx의 Node ID는 그 자체로 Public Key로 작용하여 Elliptic Curve Integrated Encryption (ECIES)를 거친 후 authenticated TCP Connection 형성에 사용된다.
RLPx는 Node ID를 바로 XOR Distance에 사용하지 않고, Keccak-256 Hash를 취한 후 XOR 연산한다.
RLPx TCP Connection에 성공하면, DEVp2p Protocol을 통해 2개의 Peer가 HELLO Message를 보낸다. HELLO Message에는 node ID, DEVp2p Version, client name, 지원하는 application protocol&version, port # 등이 포함된다. 이렇게 정보들을 전달받으면 두 개의 node는 DEVp2p Protocol을 통해 application data packet을 주고받게 된다.
이렇게 데이터를 주고받지 않는 inactive한 상태에서의 node는 DEVp2p PING Message를 주기적으로 보내 연결된 peer들이 active한지를 확인하게 된다. 만약 그렇지 못하다면 DISCONNECT Message를 보내 연결을 중단시킨다.
Ethereum Subprotocol
DEVp2p 위에서 돌아가면서, Ethereum Blockchain으로부터 정보를 수집하고 저장하는 프로토콜 (Smart Contract 제외)
DEVp2p HELLO Handshake 이후 2개의 Ethereum Peer는 STATUS Message를 보낸다. STATUS Message에는 protocol 버전, network ID, genesis hash 등 해당 node의 blockchain에 대한 현재 정보를 보낸다. 위 정보들을 통해 어떤 Peer에 연결해야 하는지를 결정한다.
STATUS 정보 교환 이후에도 연결이 유지된다면 Most Recent Block Hash (= Best Hash)와 Blockchain의 Total Difficulty를 이용하여 Synchronization을 수행한다. (GET_BLOCK_HEADERS -> 빠진 Blocks에 대해 GET_BLOCK_BODIES)
Ethereum Blockchain Validation은 Header Validation, State Validation, 그 둘을 pivot을 기준으로 섞어서 사용하는 fast sync 등이 존재한다.
IV. Case Study & Implementation: DEVp2p NodeFinder
대표적인 Ethereum Implementation인 Geth와 Parity 각각의 고유한 특징들을 알아보는 Case Study 후, 연구에 필요한 특성들을 기반으로 DEVp2p NodeFinder를 구현하기까지의 주요 포인트들을 소개한다.
이렇게 3개의 Protocol을 통해 데이터를 주고받는 Ethereum은 Message Format이나 Functionality를 특정할 수 있었지만, Message Sending Behavior의 경우 그렇지 못하다. 그리고 이러한 Behavior가 달라질 때 Network에 다양한 영향을 줄 수 있게 된다. 이것이 Geth나 Parity와 같은 다양한 Client가 등장하게 된 계기이다.
Geth와 Parity를 1주간 돌려보며 얻은 Observations으로는 다음과 같다;
Geth나 Parity는 각각 최대 25, 50개의 동시 Peer 연결이 가능하다. 다시 말해 Scanner는 이러한 Limit에서 자유로워야 한다. (다행히 Geth나 Parity에서 Scanner을 연결하는 데에는 큰 문제가 없는 듯 하다.)
Node가 갓 Blockchain과 Sync되었다면, 전 네트워크가 TRANSACTIONS 메세지로 도배된다. 이때, Geth는 모든 peer에 TRANSACTIONS을 보내지만, Parity는 root(n)만큼의 peer에게만 TRANSACTIONS를 보낸다. 만약 Scanner를 달게 된다면 TRANSACTIONS와 같은 Duplicate Messages로 인한 Bottleneck을 경험하게 될것이다.
DISCONNECT Message를 분석한 결과 Too Many Peers가 Reason으로 가장 많이 나왔다. Scanner는 이러한 메세지가 나와서는 안되며, 나오더라도 이를 극복할 수 있도록 천천히 받거나 최대 capacity에서도 의도적으로 재시도할 수 있도록 하여야 한다.
Geth에 비해 Parity의 Subprotocol Error Message 수가 현저히 적다. 이는 0x0b error code가 Unknown이라 메세지가 표시가 되지 않는 것이 아닐까? Parity의 부정확한 XOR Metric Implementation이 그 원인이 아닐까?
이러한 Observations를 고려하여 DEVp2p NodeFinder를 설계한다. NodeFinder의 주요한 특징으로는 다음과 같다.
NodeFinder는 Max Peer Limit을 무시한다. Max Capacity에서 새로운 연결이 도달한다면, Pending Peer Connections에서 바로 Too Many Peers 메세지를 보내 연결을 종료하여 새로운 연결을 수립할 수 있게끔 한다. 이는 DEVp2p와 Ethereum 2개의 Protocol에 모두 적용한다.
NodeFinder는 DEVp2p Handshake와 Ethereum Handshake, DAO fork block verification (via GET_BLOCK_HEADERS from Ethereum Sub-Protocol)이 끝나면 바로 연결을 끊는다.
그 후, known nodes에 주기적으로 재연결하여 liveliness, churn과 같은 properties를 얻는다. 이를 위해 Geth의 outgoing discovery mechanism을 개조하였다. Dynamic-dial을 통해 새로 연결을 수립한 node는 StaticNodes에 저장되며, 이후 30분마다 Static-dial을 통해 재연결된다. Dynamic-dial은 최대 16개, Static-dial은 제한 없이 concurrent하게 연결한다. 혹시나 Error로 인해 재시작이 필요할 수도 있으므로 각 Node에선 last-dialed timestamps를 저장하는 local database에 모든 연결 역사를 저장하여, StaticNodes list를 다시 생성할 수 있도록 한다. 또한, 가장 최근 TCP 연결 기록이 24시간 전 이전이라면 해당 address를 DB에서 삭제한다.
Peer의 Latency를 측정하기 위해 TCP Socket에서 매번 메세지를 보내거나 받을 때 smoothed rount-trip time을 계산한다.
V. Results (Measurements)
V-1. Longitudinal View
3달이라는 긴 기간 동안 하나의 Ubuntu 16.04 Machine에 30개의 NodeFinder Instance를 실행시켰다. 관찰하면서 다음과 같은 Property들을 얻을 수 있었다 Data 수집 중반까지는 Central DB에서 여전히 Scanned Targets에 대해 추적함에 따라 성능 저하가 일어났고, 중반에 일어난 급격한 감소는 13시간동안 DB의 구조적인 변화를 통한 Query Statement의 단순화 과정에 기인한다.
Internal Validation
각 노드별로 평균 약 304회의 Node Discovery Attempt (Geth는 180회)
# of Dynamic-dial Attempts & Node Discovery (얼추 비례함)
# of Nodes attempted/responded to Dynamic-dial (HELLO / DISCONNECT) 34K의 attempted nodes 중 11K가 active했으므로, 약 31.4%의 확률로 Active DEVp2p node를 만날 수 있다. 또한, 각 Instance 별 응답하는 Node의 # 또한 stable하다.
따라서 30개의 instance는 performance decline을 극복하기에 충분한 숫자였지만, 전체 네트워크 상 coverage는 알 수가 없었다. (82일간 총 455,641개의 node를 5K/day라는 균일한 속도로 찾음에도 불구하고) 또한, 5K/day라는 높은 growth rate의 원인은 새로운 node ID를 꾸준히 생성하는 abusive nodes로 인한 수치라고 가설을 설정하였다.
# of connections made between NodeFinder & bootstrap node NodeFinder static-dials가 디자인한 그대로 노드를 탐색했는지를 확인하는 지표 # of static-dials < 48 (static-dial interval이 30분이기 때문), 48보다 적은 이유는 모든 타입의 outbound connection attempt마다 static-dial을 reschedule하기 때문
검증) 새로운 node를 검출할 수 있는지 확인하는 방법으로 30개의 instance에 대해 끼리끼리 peer connections 수행하여 시간 측정, 한 노드가 나머지 29개의 노드들을 모두 발견하기까지 3~9시간 가량 소요
External Validation: ethernodes.org와 비교
24시간동안 스캔한 후, MainNet ID 1 - MainNet Blockchain's Genesis Hash만 Filtering해서 분석
# of Nodes in Intersetions of NodeFinder & Ethernodes Set
Ethernodes와 NodeFinder의 Coverage 차이 Reached Nodes / Unreached Nodes를 비교하면 두 영역 모두 NodeFinder가 넓은 Coverage를 가짐
앞의 Longitudinal View의 연장선으로 거시적인 Ethereum Network의 특정을 다룬다.
Non-Productive Peers Useless Peers에 연결 시도를 꾸준히 시도하게 만드는 Ethereum Eco-System 특성상 유의미한 수준의 Network Noise가 발생하게 된다. 이는 Ethereum 특유의 flexible design과 Application-Level Isolation에 따른 결과이다.
Client Heterogenity (Diversity of Client & Version)
Geth와 Parity의 Version 정보를 받아온 결과 다음과 같은 Version별 Client Popularity를 얻을 수 있었다. 이를 통해 각 Client의 Development Cycle을 평가할 수 있다. 특히 Development Cycle이 비교적 단순한 Geth의 경우 매 업데이트마다 update의 population이 증가하고, outdated version의 population이 감소하는 부분이 뚜렷하게 나타난다. 또한, 많은 수의 Client는 여전히 outdated된 version을 사용하고 있음을 확인할 수 있고, 그 수는 1K에 달한다.
Geth/Parity Friction Geth와 Parity 간의 RLPx 연결을 검증하는 동안, XOR Metric의 불일치를 확인할 수 있었다. Geth의 경우 두 개의 Node ID의 Keccak-256 Hash를 계산한 후, 두 Hash의 Log Distance를 올바르게 계산한다. Parity의 경우 좀 다른데, 두 Hash의 XOR value를 계산한 후, 각 byte의 log distance를 sum하여 최종 distance를 계산한다.
구현 과정이 다르기에, 같은 두개의 Hash에 대해서 서로 다른 Distance를 내놓는다. 100K개의 Random Distance Calculations 결과 Geth는 Exponential한 형태를 가지지만, Parity는 넓은 범위의 Binomal Distribution 형태를 띄게 된다. 이러한 차이는 Geth와 Parity 사이의 호환성에는 문제가 없지만, RLPx 노드 탐색 성능을 저하시키는 요인이 될 수 있다. 최악의 경우 다음과 같은 loop에 빠져버리는 Unintentional Eclipse Attack이 일어날 수 있다. (실제로 네트워크 상에서 다음과 같은 공격이 이루어지는지에 대해서는 밝혀진 바가 없다.)
V-3. Single Snapshot View
24hr 주기의 Snapshot은 타 P2P Network와의 비교에 적절하다. 그러나, 네트워크의 특정 순간에서의 property를 알아보기 위해 1hr 주기의 Snapshot에서 얻은 8309개 Node 중 non-Classic Ethereum Mainnet Nodes에 대응되는 6760개의 Node를 Dataset으로 하여 다음과 같은 Property들을 얻을 수 있었다.
Network Size (24hr 주기)External Validation과 동일한 시간에 진행되었으며 Abusive Nodes를 제거한 결과 과거의 Tool의 2.3배에 달하는 노드들을 찾아내었다. 그러나 Gnutella에 비하면 적은 노드 수를 관찰할 수 있었다.
Geography & Network Distribution (1hr 주기) Ethereum의 Geographic & Network Distribution은 Ethereum에서 가장 영향력있는 AS와 나라가 어디인지를 알려준다. 이를 통해 나라의 관점에서는 미국>중국>..., AS의 관점에서는 Amazon>Alibaba>Digital Ocean>... 즉 Cloud Environment에서 Ethereum이 돌아가고 있음을 알 수 있었다. Peer Latencies의 경우 직접 연결된 Node간의 Latency를 계산하기 위해 NodeFinder가 Peer Connection마다 측정한 Smoothed Round-trip Times를 측정한다. 그 이후, Gencer et al.이 사용한 Triangle Inequality Technique를 사용한 후, Bounds의 Average를 사용하여 멀리 떨어진 2개의 Node 사이 Latency를 도출했다. 위 결과를 통해 Ethereum이 Random하고 Ad-hoc한 Manner로 생성된다는 것을 확인하였다.
Client Age (1hr 주기) 위 Figure와는 반대로, Active Geth Updaters가 Parity에 비해 more up-to-date하다.
Node Freshness (1hr 주기) 특정 Analyzed Time Frame에 대해서 Peer들이 얼마나 Blockchain의 Head에 가까이 있는가를 통해 평가
V-4. Limitations
Ground-Truth Data의 부족: 발견에 대해 검증할 수 없고, 정확한 네트워크 상 coverage 추정치 또한 밝혀낼 수 없다.
High Churn & Publicly Unreachable Nodes: 분석을 더욱 어렵게 함 / Peer Connections 자체가 불가능
Non-Recoverable Measurement of Network Topology: 복구 불가
VI. Discussion
앞의 섹션에서 2가지 시선을 통해 Ethereum Network를 분석한 결과, 다양한 inefficiency과 concerns를 발견할 수 있었다. 특히 outdated된 버전을 들고 있는 clients는 미래 버전에 패치가 된 취약점과 hardfork 비호환성에 매우 취약하다. 따라서 다음과 같은 정책들을 통해 Ethereum Network를 관리할 것을 추천하였다.
Automatic Updates: 공격적인 자동 업데이트 정책
Improved Standardization: RLPx, DEVp2p, Ethereum subprotocol들은 제대로 document화되지 않았고, 이로 인해 Parity에서 XOR metric이 잘못 구현되는 등 다양한 문제점을 야기했다.
Active Monitoring: 위 논문에서 제안한 NodeFinder를 포함, Measurement Tools를 통해 네트워크의 상태를 주기적으로 파악할 것