152013년 3월 11–12일

비트코인이 두 개의 유효한 역사로 나뉘었던 밤

비트코인 코어의 새로운 버전과 오래된 버전이 하나의 블록에 대해 일치하지 않았다. 개발자들과 채굴자들은 몇 시간 안에 어느 분기를 지원할지 결정해야 했고, 그에 따른 대가를 치러야 했다.

2013년 3월 11일 저녁, 비트코인 노드들이 같은 과거에 대해 서로 다른 두 기록을 이어 가기 시작했다. 새로 나온 Bitcoin 0.8 버전은 블록 225 430을 받아들이고 그 위에 블록을 쌓았다. 반면 0.7 버전을 쓰던 일부 노드는 이를 거부했다. 처리에 필요한 데이터베이스 잠금 수가 Berkeley DB의 실제 한도를 넘었기 때문이다. 양쪽 모두 자신이 사용하는 소프트웨어를 따랐고, 각자의 체인이 유효하다고 판단했다. 처음에는 마이닝 풀에 모인 연산 능력의 대부분이 새로운 체인을 지지했다. 그냥 기다리면 분열이 해소되기는커녕 더 깊어질 가능성이 컸다. 개발자와 거래소, 풀 운영자들이 IRC에 모였다. 중앙 지휘부도 없는 상황에서, 충분히 많은 참여자가 다시 하나의 기록을 이어 갈 방법을 찾아야 했다.

01

한 개의 블록, 두 가지 판결

블록 225 430은 네트워크를 분열시키려고 만든 블록이 아니었다. 다만 이전 블록보다 트랜잭션 입력이 많아 데이터베이스에 부담을 주면서 버전 간 차이가 드러났다. LevelDB를 쓰는 Bitcoin 0.8 버전은 이 블록을 처리했다. 반면 Berkeley DB를 쓰는 0.7 버전은 데이터베이스 잠금 수의 한도에 부딪혀 오류가 발생했다.

결과는 위험하게도 미묘했다. 두 그룹의 노드들은 각자의 프로그램이 수행할 수 있는 규칙을 지키고 있었다. 새로운 가지가 의도된 합의 규칙에 따라 부정하지 않다는 것은 사실이었고, 오래된 노드들은 공격을 받지 않았다. 네트워크가 분리된 이유는 하나의 데이터베이스 기술에 대한 미리 문서화되지 않은 실제 제한 때문이었다.

0.8 버전의 채굴자들은 새로운 블록 위에 블록을 쌓았다. 구버전을 사용하는 채굴자들은 그 이전 블록부터 계속했다. 두 가지 분기 모두 추가 블록을 얻게 되면, 한 분기에서 확인된 결제가 다른 분기에는 존재하지 않을 수 있었다. 경제적으로 우세한 다수가 어떤 역사를 받아들일지 명확해질 때까지 사용자는 통상적인 확인 횟수에 안전하게 의존할 수 없었다.

새로운 가지가 처음에는 약 육십 퍼센트의 처리 능력을 가지고 있었다. 일반적인 상황에서는 더 많은 누적된 작업을 가진 체인이 우위를 점할 것이다. 그러나 여기에서는 더 오래된 노드들이 새로운 블록을 받아들이지 못했는데, 그것의 작업과는 상관없이 말이다. 재조직화 자체는 유효하지 않은 것으로 간주되는 블록을 넘어가지 않는다.

이 사건은 단순한 규칙인 "가장 긴 체인이 승리한다"의 한계를 드러냈다. 더 정확하게 말하자면, 노드가 먼저 유효하다고 인정하는 체인 중에서 가장 많은 작업을 가진 체인이 승리한다. 그러나 노드들이 유효성에 동의하지 못할 때, 해시레이트만으로는 즉시 통일을 이루기에는 충분하지 않다.

02

IRC를 임시 지휘 센터로

개발자들은 관찰하던 노드의 높이와 해시가 서로 달라지기 시작하면서 문제를 알아챘다. 소통은 IRC 채널에 집중됐고, 그곳에서 로그와 각 분기의 작업량, 대형 풀의 반응을 비교할 수 있었다. Gavin Andresen은 훗날 BIP 50에 그날 밤의 시간표와 내려진 결정들을 정리했다.

완전히 올바른 가지와 명백히 잘못된 가지 사이에서 선택할 수 없었다. 0.8 버전을 지원하는 것은 더 많은 작업을 가진 체인을 유지했지만 많은 이전 노드를 차단했다. 0.7 가지로 돌아가려면 새로운 채굴자들이 이미 합법적으로 채굴한 블록을 포기하고, 그에 대한 보상을 기대하는 것을 설득해야 했다.

실제로 돌아가게 된 이유는 호환성 때문이었다. 0.8 버전은 이전 분기에서 생성된 작은 블록을 수용할 수 있었지만, 0.7 노드는 새로운 분기의 문제가 있는 블록을 수용할 수 없었다. 성능이 두 그룹 모두에서 수용 가능한 분기에 이동한다면, 네트워크는 모든 이전 노드의 즉각적인 강제 업그레이드 없이 다시 연결될 수 있다.

이 결정은 Andresen이 프로토콜을 통해 내릴 수 있는 명령이 아니었다. 개발자들은 풀 운영자들에게 연락해 상황을 설명했고, 거래소에는 입금을 잠시 중단하라고 권고했다. Bitcoin.org도 경고를 게시하고 대형 풀에 이전 소프트웨어 버전으로 돌아가 달라고 요청했다. 양쪽 모두와 호환되는 체인에서 네트워크를 다시 합치기 위해서였다.

이 조정이 빨랐던 것은 핵심 참여자 범위가 아직 상대적으로 작았고, 사람들이 공개 채널을 통해 서로를 알고 있었기 때문이다. 이 속도는 네트워크가 더 오래 지속되는 분열로부터 구해 주었지만, 동시에 초기 운영이 의존하던 사회적 연결고리도 드러냈다. 비트코인은 수장이 없었지만, 한 통의 전화나 한 줄의 메시지로 상당한 양의 연산력을 움직일 수 있는 사람들이 있었다.

IRC 대화에는 법적 효력도, 비밀 관리자 키도 없었다. 그 의의는 서로 다른 개입 수단을 가진 사람들을 한곳에 모았다는 데 있었다. 개발자들은 원인을 설명할 수 있었고, 풀은 해시레이트를 재배치할 수 있었으며, 거래소는 예치를 중단하고, 노드 운영자는 버전을 변경할 수 있었다. 각 단계마다 고유한 위험이 있었고, 아무도 다른 사람들이 따를 것이라고 보장할 수 없었다. 따라서 0.7 버전과 호환되는 분지를 지지하기로 한 결정은 네트워크 중심부에서 내려진 기술적 명령이 아니라, 이 경로가 사용자에게 가장 적은 영향을 주며 가장 빠르게 공동의 역사를 회복할 것이라는 조정된 베팅이었다. 그 성공은 당시 커뮤니티의 작은 규모와 분리해서 볼 수 없으며, 보편적인 위기 대응 절차로 내세울 수도 없다.

03

채굴자들은 채굴한 작업을 희생한다

결정적인 행동에 나선 곳은 Eleuthria라는 이름으로 알려진 Michael Marsee의 BTCGuild와 Marek Palatinus의 Slush Pool이었다. 두 운영자는 0.8 버전에서 0.7 버전으로 되돌아가 연산 능력을 기존 체인 쪽으로 돌렸다. 이 선택은 두 체인에 누적된 작업량의 균형을 바꾸는 데 기여했다.

이것은 장부상의 무상한 제스처가 아니었다. 재조직화 과정에서 고아 분기에서 채굴된 블록들은 주 체인의 일부에서 제외되었으므로, 해당 보상도 사용 가능 상태로 남아서는 안 되었다. BIP 50는 풀들이 네트워크가 공동 분기로 복귀하도록 상당한 자금을 희생했음을 명시적으로 지적한다.

구식 지갑이 앞서가자, 0.8 업데이트된 노드는 이를 수용하고 자신의 기록을 재조직할 수 있었다. 구식 노드는 이미 그 지점에 도달했다. 분리는 하나의 버전이 다른 버전을 원격으로 덮어쓰는 것이 아니라, 충분한 수의 채굴자들이 두 버전 모두가 검증할 수 있는 지점 이후에 더 많은 작업을 생성함으로써 해결되었다.

사건 동안 적어도 한 번 큰 이중 지출이 발생했으며, 이는 계획된 공격이 아닌 실험으로 설명되었다. 그럼에도 불구하고 이 사건은 실제적인 위험을 보여주었다: 거래가 나중에 버려질 가지에서 확인될 수 있었다. 입금을 중지하라는 권고는 공황 상태가 아니었다; 이는 서비스를 일시적인 기록에 기반한 계정 잔액으로부터 보호하기 위한 것이었다.

풀은 이 시점에 네트워크를 소유하지 않았다. 채굴자들이 그들에게 위임한 해시율로 블록 순서에 영향을 줄 수는 있었지만, 모든 풀 노드가 임의의 규칙을 수용하도록 강제할 수는 없었다. 그들의 성공은 좁은 목표에 달려 있었다: 구 노드와 신 노드 모두 유효하다고 인정하는 분기를 만들고, 그 분기에 가장 많은 작업을 제공하는 것이었다.

04

승리의 신화 대신 사고 분석을 남기다

Gavin Andresen은 나중에 이 사고를 BIP 50에 정리했다. 문서는 네트워크가 아무 문제 없이 작동했다고 주장하지 않는다. 원인과 진행 과정, 버려진 블록과 이중 지출, 그리고 비슷한 불일치를 더 일찍 발견하기 위한 조치를 설명한다. 분석이 공개되면서 그날 밤의 경험은 당사자들의 기억에만 머물지 않았다. 다른 개발자들도 배울 수 있는 기록이 됐다.

한 가지 교훈은 컨센서스가 주석이나 문서에 명시된 규칙만을 포함하는 것이 아니라는 점이었다. 누구도 화폐적 의미를 의도하지 않았더라도, 광범위하게 배포된 프로그램의 실제 동작이 네트워크의 제약 조건이 될 수 있다. 따라서 데이터베이스 마이그레이션은 블록 수용에 영향을 미칠 수 있다면 단순히 내부적인 변경으로 간주해서는 안 된다.

또한 업그레이드를 더 신중하게 조정하고 운영 한계에 가까운 블록을 테스트하는 필요성이 학습되었다. 0.8 버전은 성능 개선을 가져왔지만 차이는 실제 체인에서 나타났다. 이 사건 이후 개발자들은 임시 제한을 도입하고 이전 의존성을 나중에 안전하게 포기할 수 있는 길을 준비했다.

이 이야기는 때때로 몇몇 개발자가 비트코인을 제어한다는 증거로 사용되며, 다른 때에는 자동으로 파괴할 수 없다는 증거로 사용된다. 그러나 이러한 단축된 버전은 맞지 않는다. 사람들은 긴급하게 수정을 조정할 수 있었지만, 풀, 거래소, 노드 운영자의 협력이 필요했다. 소프트웨어는 각 컴퓨터에서 별도로 결정되었으며, 공동의 결과는 그들의 결합으로 인해 나타났다.

3월 11일에서 12일로 넘어가는 밤은 하나의 메인 체인으로 돌아오며 끝났다. 하지만 차이가 저절로 사라진 것은 아니었다. 누군가는 문제를 알렸고, 누군가는 서비스를 멈췄으며, 채굴자들은 호환되는 기록을 되찾기 위해 이미 수행한 작업을 포기했다. 비트코인이 두 갈래의 과거를 극복한 데에는 규칙과 경제적 대가, 그리고 이번에는 새 소프트웨어가 물러서야 한다고 인정한 사람들이 함께 있었다.

BIP 50의 가치는 이 사고를 탈중앙화의 승리라는 말로 덮지 않는 데 있다. 구체적인 원인과 시간순 경과, 낭비된 작업, 비슷한 일을 막기 위해 계획한 조치를 기록했다. 인정하기 불편한 실패가 후속 테스트의 근거가 된 셈이다. 그래도 질문은 남는다. 서로 호환된다고 믿는 두 프로그램 버전 사이에 얼마나 많은 암묵적 전제가 숨어 있을까? 합의는 종이 위의 규칙 목록만으로 이루어지지 않는다. 독립된 구현과 노드가 경계 조건에 해당하는 경우에도 같은 결과에 도달해야 한다. 그 일치가 얼마나 중요한지는 깨지는 순간 가장 뚜렷하게 드러난다.

Atlas

아틀라스의 인물과 개념

FORK13March 2013 Chain SplitGAVINGavin AndresenSLUSHMarek ‘Slush’ PalatinusPOOLMining PoolNODEFull NodeRULES합의 규칙REORGReorg
  1. 01
    BIP 50 — 삼월 2013 체인 분기 사후 분석

    기본적인 기술 보고서에서 원인, 해시 레이트, BTCGuild와 Slush Pool의 조정, 더블 스펜드, 그리고 수정에 대해 설명하고 있다.

  2. 02
    Bitcoin.org — 11/12 삼월 2013 체인 분기 정보

    당시 거래자와 사용자를 위한 시기적 경고 메시지로 네트워크 분할 동안 발생했다.

  3. 03
    비트코인 코어 — 버전 0.8.0 릴리즈 노트

    새로운 데이터베이스 계층과 버전 변경의 맥락에서 예상치 못한 호환되지 않는 문제가 발생했다.

다음 이야기 · 16트레저: 비트코인의 키를 지갑 컴퓨터에