블록 전파 한눈에: 노드에서 노드로
앞 글에서 블록을 만드는 쪽은 수없이 시도해 조건에 맞는 해시 값을 찾았습니다. 찾은 블록은 네트워크의 다른 컴퓨터들에 전해져야 합니다 [S1]. 하지만 모두에게 한꺼번에 알릴 방송국은 없습니다. 블록은 어떻게 모두에게 닿을까요?
방송국이 없다
중앙 서버에 모두 접속 ✗
다른 노드 몇 곳과만 ✓
네트워크의 컴퓨터 하나하나(노드)는 중앙 서버에 접속하지 않습니다. 다른 노드 몇 곳1과 직접 이어져 있을 뿐입니다 [S2]. 이렇게 이어진 상대를 피어(peer)2라고 합니다. 비트코인 백서는 이 네트워크에 구조가 거의 필요 없다고 적습니다 [S1].
장부도 한곳에 없습니다. 블록과 거래를 모두 검증하는 노드(전체 노드)는 각자 검증한 블록으로 된 사슬을 갖고 있습니다 [S4]. 그러니 새 블록은 이 그물을 타고 각 노드에 닿아야 합니다.
알림, 요청, 보냄
inv · getdata · block 은 비트코인 노드가 실제로 주고받는 메시지의 이름입니다 [S3]. 해시 값은 아래 시뮬레이터의 블록에서 계산한 값입니다.알림을 먼저 보내는 방법에서는 블록을 찾은 노드가 피어마다 알림(inv)부터 보냅니다. 알림에는 블록이 아니라 블록 헤더의 해시 값이 들어 있습니다 [S2] [S3]. 알림 안에서 블록 하나를 가리키는 항목은 36바이트입니다. 무엇의 해시 값인지 적는 종류 4바이트와 해시 값 32바이트입니다 [S3]. 받은 피어는 그 값을 자기가 이미 본 값들과 견줍니다. 처음 보는 값이면 요청(getdata)을 보내고 [S3], 그러면 블록(block)이 옵니다 [S2]. 여러 피어가 같은 블록을 알려 와도 한 번만 받으면 됩니다.
알림을 먼저 보내는 방법만 있는 것은 아닙니다(표 1). 비트코인 개발자 안내서에 따르면 전체 노드 프로그램인 비트코인 코어는 연결을 맺을 때 헤더로 받겠다고 알려 둔 피어에게는 알림 대신 80바이트 블록 헤더를 바로 보냅니다 [S2] [S5]. 그 피어는 헤더만으로 확인할 수 있는 것부터 봅니다. 헤더의 해시 값이 목표 값 이하인지가 그 하나입니다. 맞으면 블록 전체를 요청합니다 [S2]. 헤더만으로 드러나는 잘못은 블록 전체를 내려받기 전에 걸러집니다.
| 방법 | 먼저 가는 것 | 받은 쪽이 하는 일 |
|---|---|---|
| 알림 먼저 | inv — 블록 헤더의 해시 값 | 처음 보는 해시 값이면 getdata 로 블록을 요청 |
| 헤더를 바로 | headers — 블록 헤더 80바이트 | 헤더부터 검증하고, 맞으면 getdata 로 블록을 요청 |
| 블록을 바로 | block — 블록 전체 | 받아 검증합니다. 블록을 찾은 쪽은 피어에게 아직 이 블록이 없음을 알아 바로 보낼 수 있습니다 |
검사한 것만 옮긴다
들은 대로 옮기지는 않습니다. 전체 노드는 받은 블록과 거래3를 모두 검증한 뒤에야 다른 노드에 전합니다 [S2]. 해시 값이 목표 값 이하인지 [S5], 블록에 든 거래가 이미 쓴 돈을 또 쓰지 않는지 따집니다 [S1]. 무엇을 따지는지는 다음 글에서 하나씩 봅니다.
통과하면 그 노드가 피어들에게 알립니다 [S2]. 통과하지 못한 블록은 전하지 않습니다. 노드 하나를 골라 두 가지 블록을 내보내 보세요. 여기서는 해시 값 조건 하나만 따집니다.
노드를 고른 뒤 아래 단추로 블록을 내보내세요.
맞는 블록은 몇 걸음 만에 그물 전체에 닿습니다. 조건에 안 맞는 블록은 처음 받은 피어들이 버려서, 그 너머의 노드는 그 블록을 보지 못합니다. 받는 노드마다 따로 검증하므로, 한 곳이 검증 없이 내보내도 그다음에서 멈춥니다. 개발자 안내서는 틀린 정보를 보내 다른 노드의 자원을 쓰게 하는 피어를 차단하는 장치가 있다고 적습니다. 점수가 기준을 넘으면 차단하고, 기본값은 24시간입니다 [S2].
놓친 블록
노드는 꺼졌다 켜지고, 알림은 도중에 사라지기도 합니다. 백서는 블록 전파는 메시지 몇 개가 빠져도 괜찮다고 적습니다. 블록 하나를 못 받은 노드는 다음 블록을 받을 때 빠진 블록을 알아차리고 요청합니다 [S1]. 새 블록의 «앞 블록의 해시 값» 이 자기가 본 적 없는 블록을 가리키면 그 사이가 빈 것입니다. 앞 블록을 받기 전에는 그 블록을 검증할 수 없습니다 [S2]. 노드 하나를 꺼서 블록을 놓치게 해 보세요.
끌 노드를 고른 뒤 차례로 누르세요: 끄기 → 다음 블록 내보내기 → 켜기 → 다음 블록 내보내기.
여기서 견줍니다: 1과 2가 같은가?
빈 곳을 메우는 방식은 둘입니다(그림 5). 시뮬레이터는 블록을 먼저 받는 방식입니다. 개발자 안내서에 따르면 비트코인 코어는 블록을 처음 내려받을 때 0.9.3 까지 이 방식을 썼고, 0.10 은 헤더를 먼저 받습니다4 [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개 통과).
곁질문
- 피어는 몇 곳인가요? 개발자 안내서는 비트코인 코어가 자기 쪽에서 먼저 거는 연결을 최대 8개로 둔다고 적습니다 [S2]. 이 글의 그물은 그리기 쉽게 서너 곳으로 줄였습니다. ↩
- 피어는 처음에 어떻게 찾고, 어떻게 이어지나요? 처음 켠 프로그램은 다른 노드의 주소를 몰라, 프로그램에 미리 적혀 있는 이름(DNS 시드)에 물어 전체 노드의 IP 주소를 받습니다 [S2]. 그 주소의 노드와 자기 버전을 알리는 메시지(
version)와 받았다는 답(verack)을 주고받으면 연결이 맺어집니다. 그 뒤로는 피어들이 다른 노드의 주소를 알려 줍니다 [S2]. ↩ - 거래도 같은 식으로 퍼지나요? 네. 거래도 알림(
inv)을 보내고 요청(getdata)이 오면 거래(tx)를 보내며, 받은 노드는 거래가 유효할 때만 같은 방법으로 다시 전합니다 [S2]. 비트코인 코어는 아직 블록에 들지 않은 거래를 메모리에 모아 둡니다 [S2]. ↩ - 처음 켠 노드는 그 많은 블록을 어떻게 받나요? 전체 노드는 첫 블록 다음부터 지금까지의 블록을 모두 내려받아 검증해야 합니다 [S2]. 비트코인 코어는 헤더를 2,000개씩 먼저 받아 확인한 뒤, 블록은 여러 피어에게 나눠 요청합니다. 한 피어에게는 한 번에 16개까지만 요청합니다 [S2]. ↩
출처
- [S1] Nakamoto, Bitcoin: A Peer-to-Peer Electronic Cash System (2008), 초록·§5 Network.
- [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.
- [S3] Bitcoin Developer Reference — P2P Network, Data Messages(인벤토리) · Inv · GetData · Block.
- [S4] Bitcoin Developer Guide — Block Chain.
- [S5] Bitcoin Developer Reference — Block Chain, Block Headers.