블록 전파 한눈에: 노드에서 노드로

점들이 선으로 이어져 고리 모양 그물을 이루고 한가운데는 비어 있다. 왼쪽 위의 노란 점 하나에서 시작한 노란색이 선을 따라 이어진 점들로 번지고 있고, 먼 쪽 점들은 아직 흰색이다.

앞 글에서 블록을 만드는 쪽은 수없이 시도해 조건에 맞는 해시 값을 찾았습니다. 찾은 블록은 네트워크의 다른 컴퓨터들에 전해져야 합니다 [S1]. 하지만 모두에게 한꺼번에 알릴 방송국은 없습니다. 블록은 어떻게 모두에게 닿을까요?

방송국이 없다

그림 1. 비트코인 네트워크에는 모두가 접속하는 중앙 서버가 없습니다. 노드마다 다른 노드 몇 곳과만 이어져 있습니다 [S2].

네트워크의 컴퓨터 하나하나(노드)는 중앙 서버에 접속하지 않습니다. 다른 노드 몇 곳1과 직접 이어져 있을 뿐입니다 [S2]. 이렇게 이어진 상대를 피어(peer)2라고 합니다. 비트코인 백서는 이 네트워크에 구조가 거의 필요 없다고 적습니다 [S1].

장부도 한곳에 없습니다. 블록과 거래를 모두 검증하는 노드(전체 노드)는 각자 검증한 블록으로 된 사슬을 갖고 있습니다 [S4]. 그러니 새 블록은 이 그물을 타고 각 노드에 닿아야 합니다.

알림, 요청, 보냄

두 노드 사이에 오가는 메시지 셋 블록을 찾은 노드 A 가 피어 노드 B 에게 inv 메시지로 «종류는 블록» 이라는 표시와 블록의 해시 값을 알린다. B 는 처음 보는 값이면 getdata 메시지로 그 블록을 요청한다. A 가 block 메시지로 블록을 보낸다. 그 뒤 B 가 블록을 검증한다. 블록을 찾은 A 피어 노드 B ① inv — 이 블록 있어요 종류 «블록» + 해시 값 008da90370… ② getdata — 처음 보는 값이에요 그 블록 주세요 ③ block — 블록 전체 앞 블록의 해시 값 · 기록 · 논스 ④ B 가 검증
그림 2. 새 블록이 피어 한 곳으로 건너가는 차례입니다 [S2]. inv · getdata · block 은 비트코인 노드가 실제로 주고받는 메시지의 이름입니다 [S3]. 해시 값은 아래 시뮬레이터의 블록에서 계산한 값입니다.

알림을 먼저 보내는 방법에서는 블록을 찾은 노드가 피어마다 알림(inv)부터 보냅니다. 알림에는 블록이 아니라 블록 헤더의 해시 값이 들어 있습니다 [S2] [S3]. 알림 안에서 블록 하나를 가리키는 항목은 36바이트입니다. 무엇의 해시 값인지 적는 종류 4바이트와 해시 값 32바이트입니다 [S3]. 받은 피어는 그 값을 자기가 이미 본 값들과 견줍니다. 처음 보는 값이면 요청(getdata)을 보내고 [S3], 그러면 블록(block)이 옵니다 [S2]. 여러 피어가 같은 블록을 알려 와도 한 번만 받으면 됩니다.

알림을 먼저 보내는 방법만 있는 것은 아닙니다(표 1). 비트코인 개발자 안내서에 따르면 전체 노드 프로그램인 비트코인 코어는 연결을 맺을 때 헤더로 받겠다고 알려 둔 피어에게는 알림 대신 80바이트 블록 헤더를 바로 보냅니다 [S2] [S5]. 그 피어는 헤더만으로 확인할 수 있는 것부터 봅니다. 헤더의 해시 값이 목표 값 이하인지가 그 하나입니다. 맞으면 블록 전체를 요청합니다 [S2]. 헤더만으로 드러나는 잘못은 블록 전체를 내려받기 전에 걸러집니다.

표 1. 새 블록을 피어에게 알리는 세 가지 방법 [S2]
방법먼저 가는 것받은 쪽이 하는 일
알림 먼저inv — 블록 헤더의 해시 값처음 보는 해시 값이면 getdata 로 블록을 요청
헤더를 바로headers — 블록 헤더 80바이트헤더부터 검증하고, 맞으면 getdata 로 블록을 요청
블록을 바로block — 블록 전체받아 검증합니다. 블록을 찾은 쪽은 피어에게 아직 이 블록이 없음을 알아 바로 보낼 수 있습니다

검사한 것만 옮긴다

들은 대로 옮기지는 않습니다. 전체 노드는 받은 블록과 거래3를 모두 검증한 뒤에야 다른 노드에 전합니다 [S2]. 해시 값이 목표 값 이하인지 [S5], 블록에 든 거래가 이미 쓴 돈을 또 쓰지 않는지 따집니다 [S1]. 무엇을 따지는지는 다음 글에서 하나씩 봅니다.

통과하면 그 노드가 피어들에게 알립니다 [S2]. 통과하지 못한 블록은 전하지 않습니다. 노드 하나를 골라 두 가지 블록을 내보내 보세요. 여기서는 해시 값 조건 하나만 따집니다.

시뮬레이터 ① · 질문: 조건에 안 맞는 블록 하나를 내보내면 어디까지 퍼질까요?

노드를 고른 뒤 아래 단추로 블록을 내보내세요.

그림 3. 그물은 노드 14개로 줄여 그렸고, 받은 노드는 «해시 값이 0 두 자리로 시작» 하는지만 따집니다. «조건에 안 맞는 블록» 은 맞는 블록의 논스만 1 올린 것입니다. 내보내는 노드는 검증하지 않고 내보낸다고 두었습니다. 걸음 사이의 시간은 실제 전파 속도가 아닙니다.

맞는 블록은 몇 걸음 만에 그물 전체에 닿습니다. 조건에 안 맞는 블록은 처음 받은 피어들이 버려서, 그 너머의 노드는 그 블록을 보지 못합니다. 받는 노드마다 따로 검증하므로, 한 곳이 검증 없이 내보내도 그다음에서 멈춥니다. 개발자 안내서는 틀린 정보를 보내 다른 노드의 자원을 쓰게 하는 피어를 차단하는 장치가 있다고 적습니다. 점수가 기준을 넘으면 차단하고, 기본값은 24시간입니다 [S2].

놓친 블록

노드는 꺼졌다 켜지고, 알림은 도중에 사라지기도 합니다. 백서는 블록 전파는 메시지 몇 개가 빠져도 괜찮다고 적습니다. 블록 하나를 못 받은 노드는 다음 블록을 받을 때 빠진 블록을 알아차리고 요청합니다 [S1]. 새 블록의 «앞 블록의 해시 값» 이 자기가 본 적 없는 블록을 가리키면 그 사이가 빈 것입니다. 앞 블록을 받기 전에는 그 블록을 검증할 수 없습니다 [S2]. 노드 하나를 꺼서 블록을 놓치게 해 보세요.

시뮬레이터 ② · 질문: 블록 하나를 놓친 노드는 어떻게 알아차리고 메울까요?

끌 노드를 고른 뒤 차례로 누르세요: 끄기 → 다음 블록 내보내기 → 켜기 → 다음 블록 내보내기.

      여기서 견줍니다: 1과 2가 같은가?

      1새 블록이 가리키는 «앞 블록의 해시 값»
      2내 사슬 끝 블록의 해시 값

      그림 4. 블록 번호(100, 101 …)는 이 글이 든 예이고, 해시 값은 블록마다 조건에 맞는 논스를 찾아 계산한 값입니다. 개발자 안내서는 «앞 블록의 해시 값» 이 노드가 아직 못 본 블록 헤더를 가리키는지를 기준으로 적습니다 [S2]. 사슬이 한 줄뿐인 이 그물에서는 그 값을 내 사슬 끝 블록의 해시 값과 견주면 됩니다. 빠진 블록을 받는 차례는 그림 5 의 «블록을 먼저» 쪽입니다 [S2].

      빈 곳을 메우는 방식은 둘입니다(그림 5). 시뮬레이터는 블록을 먼저 받는 방식입니다. 개발자 안내서에 따르면 비트코인 코어는 블록을 처음 내려받을 때 0.9.3 까지 이 방식을 썼고, 0.10 은 헤더를 먼저 받습니다4 [S2].

      놓친 블록을 메우는 두 가지 차례 블록을 먼저 받는 방식: 노드가 getblocks 로 없는 블록을 묻고, 피어가 inv 로 알림을 500개까지 보내고, 노드가 getdata 로 요청해 block 을 받는다. 헤더를 먼저 받는 방식: 노드가 getheaders 로 헤더를 묻고, 피어가 headers 로 헤더를 2,000개까지 보내고, 노드가 getdata 로 요청해 block 을 받는다. 블록을 먼저 헤더를 먼저 노드피어 노드피어 ① getblocks없는 블록을 물음 ② inv알림 500개까지 ③ getdata그 블록들을 요청 ④ block앞에서부터 검증 ① getheaders헤더를 물음 ② headers헤더 2,000개까지 ③ getdata헤더가 맞는 블록을 요청 ④ block검증
      그림 5. 놓친 블록을 메우는 두 가지 차례입니다 [S2]. 헤더를 먼저 받는 노드는 앞 블록을 모르는 블록을 받지 않을 수 있습니다 [S2].

      블록을 먼저 받는 방식의 노드는 앞 블록을 모르는 블록을 검증하지 않고 보관합니다. 그리고 그 블록을 보낸 피어에게 자기에게 없는 블록을 묻습니다(getblocks). 피어가 한 번에 알려 주는 블록은 500개까지입니다 [S2]. 헤더를 먼저 받는 방식의 노드는 헤더부터 물어(getheaders) 한 번에 2,000개까지 받습니다. 헤더마다 앞 블록을 가리키므로, 그 뒤에 요청해 받는 블록은 앞 블록을 이미 아는 블록입니다. 그런데도 앞 블록을 모르는 블록이 오면 바로 버립니다 [S2].

      그래서 같은 장부가 된다

      맞는 장부를 정해 주는 곳은 없습니다. 각 노드가 검증을 통과한 블록만 잇고 전하므로, 여러 노드의 사슬에 같은 블록이 들어갑니다. 개발자 안내서는 이때 그 노드들이 합의에 이르렀다고 말합니다 [S4]. 두 블록이 거의 같은 때에 퍼져 사슬이 잠깐 갈라지는 일은 이중 지불 글에서 다뤘습니다. 노드마다 규칙이 달랐던 날의 일은 다음 글의 곁가지에 있습니다.

      이 글의 계산과 확인

      시뮬레이터의 블록은 «앞 블록의 해시 값 · 기록 · 논스» 를 SHA-256 에 한 번 넣습니다. 실제 비트코인은 80바이트 블록 헤더를 SHA-256 에 두 번 넣고 목표 값과 견줍니다. 받은 노드는 해시 값을 다시 계산해 조건과 견줍니다. 실제 전체 노드가 따지는 나머지 규칙(앞 블록, 거래)은 넣지 않았습니다. SHA-256 은 이 글이 직접 짠 코드입니다 (교육용 — 실제로 쓰지 말 것).

      그림 2 의 차례는 표 1 의 «알림 먼저» 이고, 개발자 안내서의 이름은 «Standard Block Relay» 입니다. «헤더를 바로» 는 «Direct Headers Announcement», «블록을 바로» 는 «Unsolicited Block Push» 입니다 [S2]. «알림 먼저» 에서도 헤더를 먼저 받는 노드는 getheaders 를 보낸 뒤 getdata 를 보냅니다 [S2]. 헤더를 먼저 받는 노드는 «블록을 바로» 로 온 블록도 앞 블록을 모르면 버립니다 [S2]. 어느 방법이든 전체 노드는 블록을 검증한 뒤 피어에게 알립니다 [S2]. 연결 수 8개·차단 24시간 같은 기본값과 세 가지 방법은 안내서 [S2] 에 적힌 것이고, 그 문서가 드는 비트코인 코어의 판은 0.9.3 · 0.10 · 0.12 · 0.13 입니다.

      해시 값은 Node 의 crypto 와 교차 확인했습니다. 맞는 블록은 어느 노드에서 내보내도 모든 노드에 닿는지, 걸음 수가 그물에서의 거리와 같은지, 조건에 안 맞는 블록은 내보낸 노드의 피어에서 멈추는지도 시험했습니다. 시뮬레이터 ② 의 블록 101 부터는 앞 블록의 해시 값을 적고 같은 조건으로 논스를 찾은 블록입니다. 꺼 둔 노드가 다시 켜진 뒤 받은 블록에서 두 값이 다른지, 빠진 블록을 메운 뒤 모든 노드의 사슬이 같아지는지도 시험했습니다 (verify.mjs 73개 통과).

      곁질문
      1. 피어는 몇 곳인가요? 개발자 안내서는 비트코인 코어가 자기 쪽에서 먼저 거는 연결을 최대 8개로 둔다고 적습니다 [S2]. 이 글의 그물은 그리기 쉽게 서너 곳으로 줄였습니다. ↩
      2. 피어는 처음에 어떻게 찾고, 어떻게 이어지나요? 처음 켠 프로그램은 다른 노드의 주소를 몰라, 프로그램에 미리 적혀 있는 이름(DNS 시드)에 물어 전체 노드의 IP 주소를 받습니다 [S2]. 그 주소의 노드와 자기 버전을 알리는 메시지(version)와 받았다는 답(verack)을 주고받으면 연결이 맺어집니다. 그 뒤로는 피어들이 다른 노드의 주소를 알려 줍니다 [S2]. ↩
      3. 거래도 같은 식으로 퍼지나요? 네. 거래도 알림(inv)을 보내고 요청(getdata)이 오면 거래(tx)를 보내며, 받은 노드는 거래가 유효할 때만 같은 방법으로 다시 전합니다 [S2]. 비트코인 코어는 아직 블록에 들지 않은 거래를 메모리에 모아 둡니다 [S2]. ↩
      4. 처음 켠 노드는 그 많은 블록을 어떻게 받나요? 전체 노드는 첫 블록 다음부터 지금까지의 블록을 모두 내려받아 검증해야 합니다 [S2]. 비트코인 코어는 헤더를 2,000개씩 먼저 받아 확인한 뒤, 블록은 여러 피어에게 나눠 요청합니다. 한 피어에게는 한 번에 16개까지만 요청합니다 [S2]. ↩

      출처

      1. [S1] Nakamoto, Bitcoin: A Peer-to-Peer Electronic Cash System (2008), 초록·§5 Network.
      2. [S2] Bitcoin Developer Guide — P2P Network, Introduction · Peer Discovery · Connecting To Peers · Initial Block Download · Block Broadcasting · Orphan Blocks · Transaction Broadcasting · Memory Pool · Misbehaving Nodes.
      3. [S3] Bitcoin Developer Reference — P2P Network, Data Messages(인벤토리) · Inv · GetData · Block.
      4. [S4] Bitcoin Developer Guide — Block Chain.
      5. [S5] Bitcoin Developer Reference — Block Chain, Block Headers.