2025.12.19 - [분류 전체보기] - 비트코인이 동시에 채굴된다면? - chain reorg
비트코인이 동시에 채굴된다면? - chain reorg
2025.12.08 - [분류 전체보기] - 비트코인 채굴과 작업증명과(PoW) 비트코인 채굴과 작업증명과(PoW)가끔 뉴스를 보다보면 비트코인 솔로 채굴에 성공하여 3BTC 가량을 독식했다며 부러움을 자아낸다. (
3min-bitcoin.tistory.com
비트코인 체인 분리가 발생하고 reorg가 발생하는 원인에 대해서 알아봤다.
체인 분리가 발생하는 이유는, 1) 거의 동시에 채굴된 다음, 2) 네트워크 지연에 따라 전파 속도가 늦어서 다른 버전의 블록을 갖게 되는게 문제였다.
BIP-152는 Stale Block을 해결하고자 개발된건 아니지만, 간접적으로 많이 개선이 되었기에 같이 알아보고자 한다.
(네트워크 지연이 개선되면 간발의 차이로 조금 더 먼저 채굴한 블록이 더 빠르게 노드에 퍼질 것이기 때문이다)
BIP-152 Motivation
기존의 블록 전파(P2P relay) 방식은 비효율적이었다.
수신 노드가 이미 블록의 트랙잰션 정보를 멤풀에 갖고 있는 경우가 많음에도 송신 노드는 해당 트랜잭션을 전부 전송하는 방식이었다.
많은 데이터를 보내야 하는 노드에서 버퍼-블로트(buffer-bloat) 현상으로 가정용 인터넷 연결이 일시적으로 불가능해지는 상황이 있었고, 이로 인해 블록 전파 지연도 발생해, 주변 노드로 블록이 느리게 전파되었다.
버퍼 블로트란, 쉽게 말하면 비트코인 데이터 다운로드, 유튜브 영상 시청을 동시에 하는데, 비트코인 데이터 다운받느라 유튜브 영상이 끊키고 느려지는 현상을 말한다.
따라서 BIP-152는 블록 전파 시 사용되는 대역폭을 줄이는 것을 목표로 한다.
이로 인해 전파 지연도 같이 해결되는 상황을 누릴 수 있다. (Stale Block 발생 빈도 하락)
Version 1 - Intended Protocol Flow
세그윗 기준으로 버전 1, 2가 나뉘는데, 버전 1에 주요 내용이 다 있고, 버전 2는 세그윗 대응이고, 내용이 바뀌는건 없다.

위 그림은 3가지 전파 방식을 설명한다. 블록이 Node A에게 전파되었을 때, Node A가 Node B로 전파하는 상황에 대해서 설명한다.
A. 기존 전파 방식: A는 헤더 정보나 inv(헤더 해시값, 타임스탬프 서버)값을 B에게 보내서, 내가 이런 데이터를 가지고 있다는 것을 알려준다. 그러면 B는 해당 블록에 대한 “모든” 데이터를 달라고 요청(getdata)한다.
이런 기존(legacy relaying) 방식의 문제는, B의 멤풀에 이미 존재하는 트랜잭션도 또 보낼 수 있다는 점이다.
따라서, BIP-152에서는, 이를 해결하기 위해 두 가지 방식을 제안한다. 고대역폭 모드와 저대역폭 모드로 나뉘는데, 결국 핵심은 필요한 데이터만 달라고 요청하는 것이다.
B. 고대역폭 모드: 우선 A, B 피어가 연결되면, 수신노드인 B는 A에게 sendcmpct값을 1로 설정한다.
sendcmpct란 compact block을 어떻게 받을지 약속값을 전달하는 메세지이다.
그래서 1로 설정해서 노드 A에게 보내면 고대역폭 모드로 받겠다는 뜻이 된다.
A가 다른 노드로부터 블록을 전파받고 B에게 또 전파해야하는 하는 상황이라면 cmpctblock 메세지를 보낸다.
cmpctblock 메세지에는 짧은 트랜잭션 ID(short transaction IDs) 를 포함한다.
이는 8바이트로 압축된 txid이다. (자세한 short transaction id에 대해서는 아래에 별도 설명)
그림을 보면 노드 A 라인의 굵은 세로 직사각형이 블록 검증인데, 고대역폭 모드에서는 블록 검증이 끝나기 전에 cmpctblock을 보낸다.
노드 B는 cmpctblock의 짧은 트랜잭션 ID를 받아서, getblocktxn으로 본인 멤풀에 없는 트랜잭션들만 골라서 트랜잭션 정보 요청(getblocktxn)을 한다.
노드 A는 이를 받아서 blocktxn으로 요청받은 트랜잭션들만 골라서 정보를 전송한다.
C. 저대역폭 모드: 이는 수신노드인 B가 sendcmpct값을 0으로 보내며 두 연결이 시작된다.
A노드는 legacy relay와 동일하게 headers나 inv 를 보낸다.
그리고 수신노드인 B는 MSG_CMPCT_BLOCK 형식의 getdata 요청을 보내서 short transaction IDs를 받는다.
그러면 A는 B에게 cmpctblock을 보내고 그 이후는 고대역폭 모드와 동일하게 동작한다.
내 노드의 BIP-152 대역폭 설정
내 비트코인 노드의 실행중인 컨테이너에 접속해서 bitcoin-cli getpeerinfo 를 실행해보면 아래와 같은 json 뭉치를 확인할 수 있다.
{
"id": 940527,
"inbound": true,
...
"bip152_hb_to": false,
"bip152_hb_from": false,
...
"startingheight": 0,
"connection_type": "inbound",
"transport_protocol_type": "v1",
"session_id": ""
}
다른 내용은 다 생략했고, bip152_hb_to, from 두 가지 값이 있다.
true면 고대역폭이고, false면 저대역폭이다.
이 설정을 내가 직접 할 수 있는건 아니고, 비트코인 소스코드에 다 자동으로 설정이 되도록 되어있다.
// net_processing.cpp 파일의 1258번째 라인
...
m_connman.ForNode(nodeid, [this](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
m_connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [this](CNode* pnodeStop){
MakeAndPushMessage(*pnodeStop, NetMsgType::SENDCMPCT, /*high_bandwidth=*/false, /*version=*/CMPCTBLOCKS_VERSION);
// save BIP152 bandwidth state: we select peer to be low-bandwidth
pnodeStop->m_bip152_highbandwidth_to = false;
return true;
});
lNodesAnnouncingHeaderAndIDs.pop_front();
}
MakeAndPushMessage(*pfrom, NetMsgType::SENDCMPCT, /*high_bandwidth=*/true, /*version=*/CMPCTBLOCKS_VERSION);
// save BIP152 bandwidth state: we select peer to be high-bandwidth
pfrom->m_bip152_highbandwidth_to = true;
lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
return true;
});
위 소스코드를 요약하면, 고대역폭 피어는 최대 3개까지만 설정이 가능하다.
이에 따라서, 가장 최근에 나에게 유효 블록을 준 피어가 고대역폭 피어 집합에 새롭게 등록되고,
이미 3개였다면 가장 예전에 등록됬던 피어를 저대역폭 피어로 강등한다. (LRU 느낌)
참고문헌
https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki
bips/bip-0152.mediawiki at master · bitcoin/bips
Bitcoin Improvement Proposals. Contribute to bitcoin/bips development by creating an account on GitHub.
github.com