[Bitcoin] - ch9-2. 채굴과 합의(2)

‍허진·2023년 3월 8일
0

Blockchain

목록 보기
11/19
post-thumbnail

본 글은 '비트코인, 공개 블록체인 프로그래밍(Andreas M. Antonopoulos 저, 최은실, 김도훈, 송주한 옮김, 2018)'을 바탕으로 작성되었습니다.

이전 글에서 블록체인의 분산화된 합의 과정이 네 가지의 프로세스에 의해 이루어진다고 하였다.

  1. 포괄적인 판단 기준에 근거하여 모든 풀 노드가 각 거래마다 독립된 검증 실시
  2. 작업증명 알고리즘을 통해 증명된 계산법을 사용하여 채굴 노드들이 검증된 거래들을 새로운 블록에 독립적으로 추가
  3. 모든 노드들이 새 블록을 독립적으로 검증한 후 체인에 블록을 연결
  4. 모든 노드가 작업증명을 통해 이루어진 최고 누적 연산 체인을 독립적으로 선택

이전 글에서 1번 과정까지 설명을 마쳤다. 이번 글에서는 2번의 작업증명 알고리즘부터 설명을 시작해보겠다.

> 블록 채굴하기

이제 후보 블록이 Jing의 노드에 의해서 구성되었기 때문에 Jing의 하드웨어에 있는 채굴 리그가 블록을 유효하게 만드는 작업증명 알고리즘에 대한 솔루션을 찾기 위한 블록을 '채굴'할 시간이 되었다. 비트코인 채굴 프로세스에는 SHA256 해시 함수가 사용된다.

쉽게 설명하자면 채굴은 블록 헤더를 반복적으로 해싱해서 해시 결과값이 특정 목표값과 일치할 때까지 하나의 매개변수를 변화시키는 과정이다. 해시 함수의 결과는 미리 정해질 수도 없고 특정 해시값을 생산하는 패턴이 먼저 생성될 수도 없다. 해시 함수의 이러한 특징으로 인해 특정 목표에 일치하는 해시 결과를 얻기 위한 유일한 방법은 계속 도전해서 만족하는 해시 결과가 우연히 나타날 때까지 입력값을 임의로 수정해 나가는 것이다.

> 작업증명(Proof-of-Work) 알고리즘

해시 함수는 크게 세 가지의 특성을 가지고 있다.

  1. 임의의 길이를 가진 어떠한 입력값에도 고정된 길이의 결정적 결과값을 생산한다.
  2. 특정 입력값에 대한 해시 결과값은 항상 동일하다. 대신, 입력 값이 아주 조금만 바뀌어도 전혀 다른 값을 출력한다.
  3. 출력된 결과 값을 가지고 입력값을 유추하기 힘들다. 따라서 동일한 결과값을 생성하는 두 개의 다른 입력값을 검색하는 것은 사실상 불가능하다.

SHA256를 이용하게 되면 출력값은 입력값의 크기와 상관없이 항상 256비트 길이다. 예를 들어, 'I am Jin Heo'라는 문구의 SHA256 해시를 계산하면 다음과 같다.

a7a020a66dd79a2a91f68b0da1852a8e784cbe902127f393253f5f292e415d1d

다음으로 문구 뒤에 0부터 숫자를 1씩 증가시켜가면서 붙인 후에 SHA256을 적용하면 다음과 같은 결과를 볼 수 있다.

I am Jin Heo0 => e39f0d65c4724fd34bb89914ce1d6f18a4c098f9df82bb66c61fda63199bbe96
I am Jin Heo1 => ab3ad4a69e0d5a3285cf65b27012d0c8e9be96c23af7d35a2099f970dc65bac4
I am Jin Heo2 => 3bef9d1fc870ae3989e89d34710c998e11e925de2acf74533026d277aa07e59c
I am Jin Heo3 => c1b6bc3083f62333f4b8452666449088e22fd832bfe72ec31c3dbfbbf05ee6e9
I am Jin Heo4 => ac83825cfcb6e00dac97da9da4f5b52663a2d7a92d0c0eb86ea28bc7aacc2040
I am Jin Heo5 => dc5b58521c430fd2aed1195d57881e82832c65f221a9d22236dfea0bf951fba9
I am Jin Heo6 => 05b980fcaa3ab08696f68038bfc25ab03c7e7f4b70635fcebe4736fb5c4f7726
I am Jin Heo7 => e7f66a01cdcd2ce3a019d5b2cfe8047ebff41361c140cb4dd6a7db930b702d04
I am Jin Heo8 => b9790b56d5217769b0cb4697c79777ab580d40265e37088758dd4d1b8ea02ce9
I am Jin Heo9 => ccbc36ed40d81392f1903e454c0bcd2a32fbcdcddb9787372cfcd84acde74990
I am Jin Heo10 => 96f58e70fb7b0f8efb0a1d1ad53a8959a7839c18694336fb24d6c42de117ba8a
I am Jin Heo11 => b7202220f14c44cdc1d9bb4eef2a496b1592927fbaeeec3a01ea37ac871e1ce8
I am Jin Heo12 => a3db2bfea1fd5f9212ff109c65ed972e26232ddd5716508a49ab53642a3038bd
I am Jin Heo13 => 4b150e368e03ca561dcca7edf0cfdcb1eb758149f730fe908d7ec024495cae0b
I am Jin Heo14 => 6178ebc7505ea3edc7ec39d79e80a15a2b1f5f65ed01fc2397a59cbd72a9e9cc
I am Jin Heo15 => 064cff7bc92b40722c3daf6f681bfb8760d15d3b813045f849ab9076056387d3
I am Jin Heo16 => 3a8514f93edf06bb4228d51dd2c51aeb241ec42422bf9fb11c34e85982661dd7
I am Jin Heo17 => 253e1e7521724359e8ff032c8d9f6e94a749734e1b47a5c52cc09cd57b815754
I am Jin Heo18 => f63195fb37857c9a36615ba800022e908b55baa7bec8ef602443424675626f00
I am Jin Heo19 => f762e1edd99e03f7d3d1ef199d67a7322ce4ce7e87de24f36c1bd9204787cd0e

각 문구는 완전히 다른 해시 결과값을 생성한다. 위와 같은 시나리오에서 변수로 사용된 숫자를 난스(nonce)라고 한다. 난스는 암호화 함수의 출력값을 다양하게 변화시키는 데 사용되며, 위 경우에서는 문구의 SHA256 지문을 변화시키는 데 사용되었다.

이 알고리즘에 도전하기 위해 목표값을 설정해 보자.

"0으로 시작하는 16진법 해시를 생성하는 문구를 검색해 보라."

다행히도 이 작업은 그렇게 어렵지 않다. 예시에서 'I am Jin Heo6'이라는 문구가 05b980fcaa3ab08696f68038bfc25ab03c7e7f4b70635fcebe4736fb5c4f7726인 해시를 생성하는 과정을 보여주며, 이 값은 우리의 기준에 적합하다. 해당 문구를 찾기 위해 6회의 시도가 있었다. 가능성의 측면에서 본다면 해시 함수의 출력값이 균등하게 분포되는 경우 16해시(0부터 F까지 16개의 16진수 중 하나)마다 1회씩 16진법의 접두부로 0을 가진 결과값을 찾을 것이라고 예상할 수 있다. 수치적인 관점에서 본다면 0x1000000000000000000000000000000000000000000000000000000000000000보다 작은 해시값을 검색하는 것을 의미한다. 이 기준값을 목표값(target)이라고 하며, 목적은 수치적으로 목표값보다 작은 해시를 검색하는 일이다. 목표값을 낮추게 되면 목표값보다 작은 해시를 검색하는 일은 더욱 어려워진다.

간단한 비유를 들어보자. 게임 참가자들이 반복적으로 주사위 한 쌍을 던지되 특정 목표값보다 주사위의 합이 작게 나와야 하는 게임이 있다고 가정해 보자. 첫 회에서 목표값은 12다. 주사위 2개 모두 6이 나오지 않는 이상 이기게 된다. 다음 회에서는 목표값이 11이다. 참가자들은 주사위의 합이 10보다 작게 나와야 승리하며, 이 또한 쉬운 작업이다. 몇 회를 더 진행해서 이제는 목표값이 5 미만으로 떨어지게 되었다고 가정해 보자. 이제 주사위를 던지는 횟수의 절반 이상에서 결과값이 유효하지 않게 될 것이다. 목표값이 낮아지면 낮아질수록 주사위를 던져야 하는 횟수는 기하급수적으로 증가한다. 마침내 목표값이 2(최소 가능 수)가 되면 총 36회의 경우의 수 중에서 1회, 즉 2.7%만이 승리의 결과를 얻을 수 있을 것이다.

이러한 과정은 목표값이 부여한 난이도로부터 성공하기 위해 필요한 작업량을 추정할 수 있다는 의미다. 알고리즘이 SHA256 등의 결정적 함수에 기반을 두고 있는 경우, 입력값 자체는 목표값 아래의 결과를 내기 위해서 행해져야 하는 작업의 특정량에 대한 증명이 된다. 그래서 작업증명이라는 것이다.

위의 예시에서 승리의 난스 값은 '6'이고 이 결과값은 누구나 독립적으로 검증할 수 있다. 결과값이 성공적이면 작업증명이 된 것이다. 왜냐하면 해당 난스를 찾기 위해 작업을 했다는 것이 증명되기 때문이다. 작업을 검증하기 위해서 단 1회의 해시 계산만이 필요하지만 유효한 난스를 찾는 데는 6회의 해시 계산이 실행되어야 한다. 목표값이 더 낮은 경우(난이도가 더 높은 경우) 적절한 난스를 찾는 데 더 많은 해시 계산이 소요될 것이다.

> 난이도 목표 및 목표 재설정

비트코인의 블록은 평균적으로 10분마다 생성된다. 이 생성 주기를 바탕으로 통화 발행 빈도와 거래 정산 속도가 정해진다. 수십 년 동안 이 수치들은 일정하게 유지되어야 하는데, 그 기간 동안 컴퓨터의 계산 능력 또한 빠르게 발전할 것이다. 또한 채굴에 참여하는 참가자와 그들이 사용하는 컴퓨터의 수 또한 증가할 것이다.

해당 블록의 생성 시간을 10분으로 유지하기 위해서 채굴의 난이도는 조정되어야 한다. 사실상 작업증명 목표값은 동적인 매개변수로, 10분이라는 블록의 간격 목표를 충족하기 위해 주기적으로 조정된다. 즉, 목표값은 현재의 채굴 능력이 블록 생성 시간을 10분으로 하는 값으로 설정된다.

하지만 완벽하게 분산화된 네트워크 내에서 어떻게 조정이 이루어질까? 재설정은 자동적으로 노드 전부에서 독립적으로 실행된다. 2,016개의 블록바다 노드는 전부 작업증명을 재설정한다. 재설정 방정식을 통해 마지막 2,016번째 블록을 찾는 데 걸리는 시간을 측정하고 측정 시간과 20,160분(블록 간격 10분을 기준으로 2,016블록의 시간)이란느 예상 시간을 비교한다. 실제 시간과 원하는 시간 사이의 비율이 계산되고 대응되는 조정값(상승 혹은 하락)이 난이도를 결정한다.

방정식은 다음과 같이 요약될 수 있다.

New Difficulty = Old Difficulty * (Actual Time of Last 2016 Blocks / 20160 minutes)

목표값은 거래의 건수나 거래의 가치와는 무관하다는 점을 명심하자. 이는 해싱 파워의 크기와 그에 따라 비트코인을 획득하는 데 필요한 전력량 또한 거래의 건수와 무관하다는 것을 뜻한다. 비트코인은 현재의 해싱 파워를 증가시키지 않고도 규모를 키울 수 있고, 더 많이 사용될 수 있으며, 안전을 유지할 수 있다.

> 블록을 성공적으로 채굴하기

Jing의 노드는 후보 블록을 구성해서 채굴을 위한 준비를 한다. Jing은 하드웨어 채굴 리그를 이용해 믿기 힘든 속도로 SHA256 알고리즘을 가동한다. 이때 Jing의 하드웨어는 1초당 수조 개의 난스를 테스트하기 시작한다.

블록 #277316을 채굴하고 나서 약 11분 후에 Jing의 하드웨어 채굴 기계들 중 한 대가 솔루션을 찾아내고 해당 솔루션을 다시 채굴 노드로 전송한다. 난스가 블록 헤더에 삽입되고 나서 4,215,469,401인 난스 값은 다음의 블록 해시를 생성한다.

0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4

위 해시는 아래에 나오는 목표값보다 작다.

0000000000000003A30C00000000000000000000000000000000000000000000

즉시 Jing의 채굴 노드는 모든 이웃 노드들에게 해당 블록을 전송한다. 이웃 노드들은 새로운 블록을 전송받아 검증하고 난 후 전파한다. 해당 블록이 네트워크로 퍼져 나가면 각 노드는 이 블록을 본인들의 블록체인 복사본에 추가해 넣어서 블록의 높이를 277,316으로 증가시킨다. 다른 채굴 노드들이 해당 블록을 전송받아 검증하게 되면 동일한 높이에 있는 블록을 찾는 노력을 멈추고 즉시 체인 내 다음 블록을 계산하기 시작한다.

> 새 블록을 검증하기

블록을 검증하고 분산화된 블록체인을 생성하는 합의를 이끌어 낼 수 있는 최장의 체인을 선택하기 위해 노드들이 사용하는 프로세스를 살펴보자.

비트코인 합의 메커니즘의 세 번째 단계는 네트워크상에 있는 모든 노드에 의해 새 블록 각각이 독립적으로 검증받는 것이다. 솔루션을 찾은 새 블록이 네트워크로 전송되면, 노드 각각은 이웃 노드들에게 전파되기 전에 블록들을 검증하기 위해 일련의 테스트를 수행한다. 이를 통해 유효한 블록만이 네트워크상으로 전파된다. 또한 이러한 독립적인 검증을 통해 정직하게 활동하는 채굴자들이 그들의 블록을 블록체인에 연결하여 보상금을 받을 수 있도록 해 준다. (정직하지 않게 활동하는 채굴자들의 블록들은 거절당하기 때문에 그들은 어떠한 보상도 없이 전기세를 날리게 된다.)

노드가 새로운 블록을 전송받게 되면 모두 충족시켜야 하는 여러 개의 기준이 담겨 잇는 목록을 보고 체크함으로써 해당 블록을 검증하게 될 것이다. 기준 목록의 항목들을 충족하지 못하는 경우 해당 거래는 거절된다. 기준 목록은 다음과 같다.

  • 해당 블록의 데이터 구조는 문법적으로 유효하다.
  • 해당 블록 헤더 해시는 (작업증명을 시행하는) 목표값보다 작다.
  • 해당 블록의 타임스탬프는 (시간 오류를 고려해서) 향후 2시간 이내다.
  • 해당 블록의 크기는 허용할 수 있는 한도 내에 있다.
  • 제일 첫 거래는 코인베이스 거래다.
  • 블록 내에 있는 거래 전부는 거래 체크리스트를 이용해서 유효함을 판단한다.

위의 기준을 체크하는 CheckBlock 함수는 다음과 같다.

bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW)
{
    // 블록 해시가 이미 블록체인에 존재하는지 확인
    uint256 hash = block.GetHash();
    if (mapBlockIndex.count(hash))
        return state.DoS(100, false, REJECT_DUPLICATE, "duplicate");

    // 블록이 이전 블록과 정확하게 연결되어 있는지 확인
    CBlockIndex *pindexPrev;
    map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
    if (mi == mapBlockIndex.end())
        return state.DoS(10, error("%s : prev block not found", __func__), REJECT_INVALID, "bad-prevblk");

    pindexPrev = mi->second;
    if (!CheckIndexAgainstCheckpoint(pindexPrev, state, Params().GetConsensus()))
        return false;

    // 블록의 시간이 너무 멀리 떨어져 있지 않은지 확인
    if (block.GetBlockTime() > FutureDrift(GetAdjustedTime()) + 2 * 60 * 60)
        return state.DoS(10, error("CheckBlock: block timestamp too far in the future"), REJECT_INVALID, "time-too-new");

    // 블록의 논스 값이 올바른지 확인
    if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(), block.nBits, Params().GetConsensus()))
        return state.DoS(100, error("%s : proof of work failed", __func__), REJECT_INVALID, "high-hash");
    
    // 블록 내의 모든 트랜잭션이 유효한지 확인
    if (!CheckBlockTransactions(block, state, pindexPrev, Params().GetConsensus()))
        return false;

    // 블록의 크기가 최대 블록 크기를 초과하지 않는지 확인
    if (::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SERIALIZED_SIZE)
        return state.DoS(100, error("CheckBlock: size limits failed"), REJECT_INVALID, "bad-blk-length");

    // 블록의 작업 증명이 올바른지 확인
    if (fCheckPOW && !CheckBlockHeader(block, state, pindexPrev, Params().GetConsensus()))
        return false;

    // 블록을 검증한 후 새로운 블록 인덱스를 생성하고 블록체인에 추가
    CBlockIndex* pindexNew = AddToBlockIndex(block);
    ConnectBlock(block, pindexNew, state);
    return true;
}

네트워크상에 있는 노드 전부가 새 블록 각각을 독립적으로 검증하게 되면 채굴자들은 사실 여부를 속일 수가 없게 된다. 채굴자들은 모든 노드가 따르고 있는 공유 규칙에 따라 완벽한 블록을 구성해야 하며 작업증명에 대한 정확한 솔루션을 가지고 블록을 채굴해야 한다. 그렇게 함으로써 채굴에 많은 전기를 소비하게 된다. 만약 그들이 정직하지 않게 활동한다면 쏟아 부었던 전기와 노력은 허사가 된다. 그래서 독립적인 검증이 분산화된 합의의 중요한 요소가 되는 것이다.

> 블록체인을 수집해서 선택하기

비트코인의 분산화된 합의 매커니즘의 마지막 단계는 블록을 체인 안에 모아서 가장 많은 작업증명을 보유하고 있는 체인을 선택하는 일이다. 노드는 세 가지 종류의 블록을 보관하고 있다. 하나는 메인 블록체인에 연결되어 있는 블록이고 또 다른 하나는 메인 블록체인에서 나와서 브랜치를 형성하는 블록(2차 체인)이며, 나머지 하나는 알려져 있는 체인 내에서 알려진 부모가 없는 블록(고아)이다. 유효하지 않은 블록은 검증 기준 중에 하나라도 만족을 못하면 거절을 당하고 그에 따라 어떤 체인에도 포함되지 않는다.

작업증명이 가장 많이 누적된 블럭들로 구성한 유효한 체인이면 어떠한 것이라도 '메인 체인'이 된다. 대부분의 경우, 체인 내에 가장 많은 블록을 담고 있으면 메인 체인이 된다. 또한 메인 체인은 해당 체인상에 있는 블록들과 '형제 자매' 관계에 있는 블록이 들어 있는 브랜치도 보유하고 있을 것이다. 이러한 블록들은 유효하지만 메인 체인의 일부는 아니다. 해당 작업 내에서 메인 체인을 넘어서기 위해 이러한 블록들 중 하나가 확장되는 경우 나중에 참조용으로 사용하기 위해 브랜치를 유지한다.

새로운 블록을 전송 받은 노드는 새 블록의 부모블록을 가리키고 있는 '이전 블록 해시' 필드를 살펴본다. 그 후 해당 노드는 기존의 블록체인에서 해당되는 부모블록을 찾기 위한 시도를 할 것이다. 대부분의 경우 부모블록은 메인 체인의 '끝'에 위치하게 되는데, 이는 이 새로운 블록이 메인 체인을 확장했다는 것을 의미한다.

가끔 새로운 블록은 메인 체인이 아닌 체인을 확장한다. 이 경우 해당 노드는 확장하고자 하는 2차 체인에 새 블록을 붙이고 난 후 2차 체인 작업과 메인 체인 작업을 비교한다. 2차 체인이 메인 체인보다더 많은 누적 작업을 가지게 되는 경우 해당 노드는 2차 체인으로 재수렴한다. 이는 2차 체인을 메인 체인으로 선택하고 예전의 메인 체인을 2차 체인으로 바꾸는 것을 의미한다.

유효한 블록을 전송받았는데 부모블록이 현 체인에서 발견되지 않는 경우 해당 블록을 '고아'라고 한다. 고아블록은 부모 블록이 도착할 때까지 머무르는 장소인 고아블록 풀에 저장된다. 부모블록이 도착하고 기존 체인에 연결되고 나면 고아블록은 고아블록 풀에서 빠져 나와서 부모블록에 연결될 수 있다. 고아블록은 대개 두 개의 블록이 각자 짧은 시간 내에 채굴되어 반대의 순서로 도착하는 경우에 발생한다.

가장 누적 작업이 많이 이루어진 유효한 체인을 선택함으로써 마침내 모든 노드는 네트워크 전역의 합의를 이끌어내게 된다. 채굴 노드는 다음 블록을 채굴하면서 어떤 체인이 연장될지에 대해 선택함으로써 채굴 파워로 '투표'한다. 채굴 노드가 새로운 블록을 채굴해서 체인을 연장하는 경우 새 블록 자체가 채굴 노드의 투표 결과를 의미한다.

> 블록체인 분기

블록체인은 분산화된 데이터 구조이기 때문에 블록체인의 복사본들의 내용이 항상 동일한 것은 아니다. 블록들이 다른 시간에 다른 노드에 도착할 수도 있기 때문에 노드들은 블록체인에 대해 다른 관점을 가지게 된다. 이 현상을 해결하기 위해 각 노드는 항상 작업증명을 가장 많이 시행한 블록체인을 선택해서 연장하려고 하고, 이러한 체인을 최장 체인(longest chain) 혹은 최다 누적 작업 체인(greatest cumulative work chain)이라고 한다. 모든 노드가 최다 누적 작업 체인을 선택하는 한, 전 세계 비트코인 네트워크는 결국 일관된 형태로 수렴하게 된다. 블록체인의 버전 사이에서 불일치가 일시적으로 발생하면서 체인의 분기가 생기게 되는데, 더 많은 블록이 여러 분기 중 한 곳에 추가되면 결국 재수렴이 발생하면서 이 현상이 해결된다.

각 노드는 전 세계 블록체인에서 각자의 관점을 가지고 있다. 이웃 노드들로부터 블록을 수신받은 노드는 블록체인에 대한 자체 복사본을 업데이트해서 최다 누적 작업 체인을 선택하게 된다. 다음 그림에서 각 노드는 현재 메인 체인의 끝부분에 있다고 여겨지는 블록을 표현하는 모양을 포함하고 있다. 그래서 만약 노드에서 별 모양을 보았다면 그것은 해당 노드가 관여하고 있는 한 별모양 블록이 메인 체인의 끝부분에 있다는 것을 의미한다.

다음 그림에서는 네트워크가 메인 체인의 끝에 위치한 별모양 블록을 통해 단일 관점의 블록체인을 보유하고 있다.


[그림 - 블록체인 분기 사건의 시각화 : 분기가 발생하기 전]

'분기'는 두 개의 후보 블록들이 최장 블록체인을 형성하기 위해 경쟁할 때마다 발생한다. 이러한 분기는 일반적인 상황에서 두 명의 채굴자가 짧은 시간 간격을 두고 작업증명 알고리즘의 솔루션을 찾았을 때 발생한다. 유효한 블록을 전송 받은 노드 각각은 해당 블록을 블록체인에 추가하고, 이 노드가 동일한 부모블록을 연장한 또 다른 후보 블록을 만나게 되면 이 두 번째 후보 블록을 2차 체인에 연결시킨다. 결과적으로 몇몇 노드는 하나의 후보 블록을 '우선' 만나게 되고 다른 노드들은 나머지 후보 블록을 만나게 되는데, 이 두 후보 블록으로 인해 경쟁하는 블록체인 두 가지가 나오게 된다.

다음 그림에서는 두 개의 다른 블록을 채굴하는 두 명의 채굴자(A노드, B노드)가 거의 동시에 나온다. 이 두 블록 모두 별모양 블록의 자식블록이며, 이는 별모양 블록의 상부에 블록을 만들어서 체인을 연장한다. 이 과정을 추적하기 위해서 한 블록은 A노드에서 생성된 삼각형 블록으로 표시하고 나머지 한 블록은 B노드에서 생성된 역삼각형 블록으로 표시한다.


[그림 - 블록체인 분기 사건의 시각화 : 동시에 발견된 두 개의 블록]

이처럼 A노드에 있는 한 채굴자는 '삼각형' 블록에 대한 작업증명 솔루션을 찾았고, B노드의 채굴자가 거의 동시에 '역삼각형' 블록에 대한 솔루션을 찾았다고 해보자. 두 블록 모두 '별모양' 블록을 부모블록으로 가진다. 이 두 블록은 거래의 순서만 조금 다를 뿐 거의 동일한 거래를 담고 있다.

이 두 개의 블록이 전파되면서 어떤 노드에서는 '삼각형' 블록을, 어떤 노드에서는'역삼각형' 블록을 먼저 전송받게 된다. 이로 인해 네트워크는 서로 다른 두 가지 관점을 가진 블록체인으로 나뉘게 된다.


[그림 - 블록체인 분기 사건의 시각화 : 두 개의 블록이 전파되어 네트워크가 두 개로 분열]

그림에서 보면 X노드는 삼각형 블록을 먼저 수신받아 이를 이용해 별모양 체인을 연장한다. 따라서 삼각형 블록으로 메인 체인이 선택된다. 그 다음에 역삼각형 블록을 수신받았기에 역삼각형 블록은 이 경쟁에서 '패배'되었다고 간주된다. 하지만 폐기되지는 않고 별모양 부모블록에 연결되고 2차 체인을 형성한다. X노드는 자신이 승리한 체인을 정확하게 선택했다고 가정하는 동시에 '패배한' 체인을 가지고 있는다. 이 '패배한' 체인이 결국 '승리'할 경우 재수렴에 필요한 정보를 가지고 있는 것이다. 네트워크의 반대편에는 반대의 경우가 발생한다.

'옳은' 경우도 '옳지 않은' 경우도 없다. 두 경우 모두 블록체인의 유효한 관점이다. 나중에 시간이 지나고 보면 이 중 하나가 추가 작업에 의해 연장된 방법에 따라 승리하게 될 뿐이다.

그 다음으로 삼각형 블록 상부에 새 블록을 만드는 채굴자들이 '마름모' 블록을 발견했다고 치자. 채굴자들은 이 새로운 블록을 즉시 전파하고 전 네트워크는 이 블록을 유효한 솔루션으로 간주한다.


[그림 - 블록체인 분기 사건의 시각화 : 하나의 분기로 확장되는 새 블록, 네트워크 수렴]

이전 경쟁에서 승리한 체인을 참조하여 '삼각형' 블록을 선택한 모든 노드들은 블록 하나를 더 붙여서 체인을 연장할 것이다. 하지만 승리한 체인으로 '역삼각형' 블록을 선택한 경우 두 가지 체인 형태를 볼 수 있다. 하나는 별모양-삼각형-마름모 체인이고 다른 하나는 별모양-역삼각형 체인이다. 이 중 별모양-삼각형-마름모 체인이 더 길기 때문에 이를 메인 체인으로 설정하고 별모양-역삼각형 체인을 2차 체인으로 변경한다. 이것이 바로 체인 재수렴이다. 노드들이 더 길어진 체인에 대한 새로운 근거를 마련하기 위해 블록체인의 관점을 수정해야 하기 때문이다.

별모양-역삼각형 블록을 연장하는 작업을 하는 채굴자들은 이제 후보 블록의 역삼각형 부모블록이 더 이상 최장 체인이 아닌 관계로 후보 블록이 '고아'블록이 되기 때문에 이 작업을 중단할 것이다. 그리고 모든 채굴자들은 즉시 별모양-삼각형-마름모 체인을 연장하기 위해 '마름모' 블록을 부모블록으로 참조하는 후보 블록에 대한 작업을 시작한다.


[그림 - 블록체인 분기 사건의 시각화 : 새로운 최장 체인으로의 네트워크 수렴]

비트코인의 블록 생성 간격인 10분은 신속한 승인 시간(거래 정산)과 분기가 발생할 가능성 사이에서 절충해서 설계한 것이다. 블록 생성 시간이 빠르면 빠를수록 거래 처리 속도는 빨라지지만 블록체인 분기가 좀 더 빈번하게 발생하고, 블록 생성 시간이 느려지면 분기 수는 줄어들지만 거래 정산 시간도 느려진다.

채굴과 합의와 관련된 추가적인 내용들은 다음 장에서 마지막으로 알아보도록 하자.

profile
매일 공부하기 목표 👨‍💻 

0개의 댓글