제네시스 블록
제네시스 블록은 비트코인과 마찬가지로 블록체인의 최초 블록을 의미하며, 첫 번째에 위치에 생성됩니다. 특징으로는 최초의 블록으로서 이전(부모) 블록이 없기 때문에 이를 참조할 수 없고, 블록의 번호 또한 초기값인 0을 갖습니다.
그리고 처음에는 어떠한 트랜잭션도 생성되지 않았기 때문에, 제네시스 블록에는 트랜잭션이 없습니다. 추가로, 이더리움 클라이언트들은 같은 제네시스 블록을 갖고 있을 때만 서로가 같은 이더리움 네트워크에 연결되어 있다는 것을 인지하고 블록들을 싱크합니다. 그래서 제네시스 블록이 다르다면 다른 블록체인이라고 말할 수 있으며, 이는 이더리움에서 프라이빗 네트워크를 생성할 때 쓰입니다.
다음은 제네시스 블록을 생성하기 위해서 사용되고 있는 genesis.json 파일입니다. 파일에 쓰여 있는 각 필드들에 대해서 설명하겠습니다. Alloc은 이더를 특정 어카운트에 미리 할당하는 기능입니다. 이를 이용하면 마이닝 없이 명시한 어카운트에 이더를 할당시킬 수 있습니다.
Coinbase는 마이너가 보상과 수행 대가를 전송받을 어카운트 주소를 말하며, 이는 마이너가 블록을 생성할 때 자신의 주소를 coinbase로 설정하기 때문에, 제네시스 블록에서는 아무 값이나 설정해도 됩니다. Difficulty는 Nonce 값을 찾기 위해서 수행하는 계산에서 사용되는 목표값입니다.
만약 난이도가 높으면 더 많은 해시 계산을 하기 때문에 블록 생성시간이 길어집니다. ParentHash는 부모 블록 헤더의 해시값인데, 제네시스 블록의 경우는 부모 블록이 없기 때문에 0입니다. Timestamp는 해당 블록의 생성 시간이며, 유닉스 time() 함수의 값입니다.
만약 연속된 두 개 블록의 생성 시간이 길면, 마이닝 작업의 난이도를 낮춰서 블록 생성 시간을 줄일 수 있습니다. 이것 이외에도 다른 필드들이 있지만, 블록을 설명할 때 이미 언급한 것들이기 때문에 설명을 생략하도록 하겠습니다.
Uncle Block
이더리움은 합의 알고리즘을 PoS로 교체할 계획을 가지고 개발 중에 있지만, 결과적으로 아직까지는 PoW를 사용하고 있습니다. 때문에 비트코인과 마찬가지로 마이너가 작업을 증명하여 블록 생성에 성공하였고 이 블록이 다른 노드들에 브로드캐스팅되어 이상 없이 검증되었지만, 포함된 블록체인의 난이도가 낮아서 메인 블록체인에 포함되지 못하는 블록들이 발생합니다.
비트코인에서는 이렇게 메인체인에 포함되지 않는 블록을 고아 혹은 스테일 블록이라 부르고 이에 대해 아무런 보상을 해주지 않습니다.
이더리움에서는 이런 메인체인에 포함되지 않는 블록을 엉클 (Uncle) 블록이라고 부르고 처리 방법도 다릅니다. 비트코인에서는 블록 생성 시간이 10분으로 길고, 고아 블록이 많이 발생하지 않습니다. 하지만 이더리움은 블록 생성 시간이 10~20초 정도로 짧은 만큼 난이도가 낮아서 동시에 엉클 블록이 많이 생겨납니다.
엉클 블록이 많아지면 블록체인에 몇 가지 문제들이 일어납니다. 첫 번째 문제는 트랜잭션 처리를 지연시킬 수 있다는 것입니다. 왜냐하면 메인 체인에 연결되는 정상 블록에 포함된 트랜잭션들은 처리가 되어 블록체인에 저장되는 반면, 엉클 블록에 포함된 트랜잭션은 메인체인에 포함되지 못하기 때문입니다.
결국 엉클 블록에 포함 돼있는 트랜잭션의 처리는 지연됩니다. 두 번째 문제는 쓸데없는 해시 계산을 위한 컴퓨팅 자원의 낭비입니다. 각 마이너는 동일한 번호의 블록을 생성하기 위해서 각자의 컴퓨팅 자원을 사용합니다. 하지만 동시에 생성된 블록들 중 하나만 정상 블록이 되고, 나머지 블록들은 엉클 블록이 됩니다.
뿐만 아니라 블록체인이 분기되고 맨 앞의 엉클 블록 뒤에 연결된 모든 블록들이 엉클 블록이 되기 때문에, 엉클 블록들을 생성한 마이너들의 컴퓨팅 자원은 쓸데없이 낭비되는 것입니다. 세 번째 문제는 이더리움의 보안성을 약화시키는 것입니다. 엉클 블록을 생성한 마이너가 다음 블록을 계속 생성한다면, 평균 블록 생성 시간이 더 길어지게 됩니다.
그리고 블록 생성 시간이 길어지면 해당 블록 생성 후에 난이도가 줄어들게 됩니다. 난이도가 비정상적으로 줄어들게 되면 이더리움에 심각한 영향을 끼칠 수 있습니다. 난이도가 줄어들게 되면 블록 생성 타임이 짧아지게 되는데, 이렇게 된다면 컴퓨팅 파워가 높은 마이너의 영향력이 커지게 됩니다.
짧은 시간에 여러 개의 블록을 생성할 수 있기 때문인데, 이렇게 되면 강한 마이너에 의해서 블록체인이 컨트롤되는 문제가 발생할 수도 있습니다. 그림은 실제 etherscan이란 explorer 사이트를 검색하여 엉클 블록이 생긴 체인의 일부분을 그린 것입니다. 초록색 블록은 정상 블록들이고, 파란색 블록들은 메인 체인에 포함되지 못한 엉클 블록들입니다.
Ghost (Greedy Heaviest Observed Subtree)
고스트 알고리즘은 이더리움의 엉클 블록 문제를 해결하기 위한 솔루션입니다. 고스트 알고리즘은 블록 생성 시 마이너가 정상 블록에 대해 최대 2개의 엉클 블록을 추가하고 이에 따른 보상을 정상 블록의 마이너와 엉클 블록을 생성한 마이너에게 이더로 보상하여 엉클 블록의 문제를 해결합니다.
엉클 블록에 있는 트랜잭션과 컨트랙트는 블록체인에 포함되지 못하지만, 엉클 블록을 생성한 마이너에게 이더를 주어서 사용한 컴퓨팅 파워에 대해 보상을 해줍니다. 그리고 엉클 블록이 추가됨으로써 블록체인의 전체 난이도에 엉클 블록의 난이도가 포함되기 때문에 전체 난이도의 합 또한 상승합니다.
뿐만 아니라 엉클 블록이 있을 시 블록 생성 시간이 다소 길어져도 난이도를 낮추지 않습니다. 다음은 이더리움 기술문서를 기반으로 실제 이더리움에서 사용하는 고스트 알고리즘을 설명하겠습니다. 하나의 블록은 반드시 하나의 부모 블록을 지정해야 하고, 0개 이상의 엉클 블록들을 지정해야 합니다.
엉클 블록은 최대 2개까지 가능합니다. 또한, 정상 블록 B에 포함된 엉클 블록은 다음과 같은 특징들을 갖습니다. 첫째, 블록 B의 k번째 조상의 직접적인 자손이어야 합니다. 여기서 k는 2 이상 7 이하까지 가능합니다. 엉클 블록도 6개의 블록 중 하나에 포함되어야 한다는 것을 의미합니다.
둘째, 블록 B의 조상이어서는 안 됩니다. 블록 B의 조상이라는 것은 이미 메인 블록에 포함된 정상 블록을 의미하기 때문에 불가능합니다. 셋째, 엉클 블록은 유효한 블록 헤더를 가져야 하지만, 유효한 블록을 가질 필요는 없다는 것을 의미합니다. 마지막으로, 하나의 엉클 블록이 각각 다른 정상 블록에 중복해서 포함되면 안 됩니다.
기술 문서에 기술된 보상방식으로는 엉클 블록의 마이너는 정상 블록 생성 시에 받는 보상의 93.75%를 보상으로 받고, 엉클 블록이 포함된 정상 블록의 마이너는 엉클 블록 1개당 3.125%의 추가 보상을 받습니다. 이때 트랜잭션 처리 수수료는 엉클 블록의 마이너에게 지급되지 않습니다. 엉클 블록의 마이너는 해당 엉클 블록을 포함한 정상 블록과 블록 번호 차이에 따라서 상대적으로 엉클 블록 보상을 받게 됩니다.
이제 이 방식을 적용해서 실제 엉클 블록 보상에 관한 예시를 설명하겠습니다. 5473207번 블록을 보면 두 개의 엉클 블록을 포함시켰습니다. 첫 번째 엉클 블록은 5473205번 블록의 엉클 블록, 두 번째 엉클 블록은 5473204번 블록의 엉클 블록입니다. 이제 각 블록을 마이닝한 마이너가 받는 보상을 계산해보겠습니다.
정상 5473207번 블록의 마이너는 coinbase 보상으로 3이더, 트랜잭션 수수료로 약 0.1009583655이더, 그리고엉클 블록 2개를 포함시킨 보상으로 0.1875이더를 받습니다. 엉클 블록 1개당 3x0.03125 = 0.09375이더이기 때문에, 2개를 포함시켜서 0.1875가 되는 것입니다.
첫 번째 엉클 블록을 마이닝한 마이너는 엉클 블록이 포함되는 정상 블록과 2만큼 차이가 나기 때문에 3*(8-2/8) = 2.25이더를 보상으로 받습니다. 두 번째 엉클 블록을 마이닝한 마이너는 해당 엉클 블록이 포함되는 정상 블록과 3만큼 차이가 나기 때문에 3*(8-3/8) = 1.875이더를 받게 됩니다.
이더리움 상태 트랜잭션 기능
이더리움에서 트랜잭션이 블록에 포함되어 블록체인에 저장된다는 것이 뭘 의미하는 것일까요? 이더리움 상태 전이 함수 APPLY(S, TX) ->S’입니다. 이더리움의 상태 S일 때, 트랜잭션 TX를 처리한다면 상태 S’로 변하는 상태 전이가 일어난다는 것을 의미합니다.
그림을 보면 상태 S에서 Transaction을 적용했을 때 어카운트 주소 bb75a980으로 전송된 10이더가 더해져서 bb75a980는 5,202에서 5,212 이더를 보유하게 되고 Data를 받아 인덱스 2에 CHARLIE를 저장한 상태 S’로 전이가 이루어집니다.
이것은 블록에 포함되어 있는 하나의 트랜잭션을 처리했을 때 생기는 상태 전이입니다. 블록에 포함된 트랜잭션 모두를 포함된 순서에 따라서 처리하게 되며, 이에 따른 상태 변이가 계속 일어나게 됩니다. 밑의 그림은 이더리움 노드들이 마이닝 성공한 블록을 받아서 유효성 검증을 한 후 블록에 포함된 트랜잭션들을 상태에 적용시키는 모습을 보여줍니다.
처음 상태 S[0]일 때 TX[0]를 적용시켜 상태 S[1]이 되었고, 계속해서 블록에 포함된 마지막 트랜잭션인 TX[n-1]까지 적용시키면 상태 S[n]이 됩니다. 이후 블록의 마이너에게 coinbase 보상 및 수수료 보상을 완료하고 마지막 상태로 전이됩니다. 블록체인은 중개 플랫폼을 대체하며 데이터의 공유경제를 만들 수 있는 기술입니다.
이를 위해서 블록체인은 네트워크에 참여하는 모든 유저들이 데이터를 공유한다는 것이 특징입니다. 그러나 계속해서 공유하는 데이터 크기가 증가한다면 블록체인의 참여자들은 이 많은 데이터를 동기화해야 하는 큰 부담을 갖게 됩니다. 이는 다양한 기기에서 블록체인의 노드를 운영하지 못하게 하는 큰 장벽이 될 수 있습니다.
비트코인에서는 이를 위해서 트랜잭션을 머클 트리를 사용하여 처리하였습니다. 이더리움에서는 트랜잭션과 리시트는 블록에 포함되어 저장되면 더 이상 변하지 않는 데이터가 되기 때문에 비트코인과 동일하게 머클 트리를 사용할 수 있습니다. 단지, 해시함수를 SHA 256을 쓰는 게 아니라 Keccak256을 사용합니다.
머클 트리는 비트코인에서 이미 설명하였기 때문에 생략하겠습니다. 제가 여기서 설명하려는 것은 머클 패트리샤 트리라는 머클 트리를 개선한 해시 기반 트리 자료구조입니다. 앞에서 언급한 트랜잭션과 리시트는 변하지 않지만, 이더리움에는 어카운트 정보를 포함하고 있는 상태는 트랜잭션이 적용될 때마다 변경됩니다. 어카운트는 생성되거나 변경될 수 있고 잔액이나 스토리지기가 트랜잭션에 의해 동적으로 변하기 때문입니다.
참고로, 상태는 key와 value로 이루어져 있는 map 구조입니다. 이더리움에서는 이와 같은 상태의 특징 때문에 상태 트리의 경우는 머클 트리에 패트리샤 트리의 특징을 결합한 머클 패트리샤 트리를 사용하고, 이때 트리 내의 모든 데이터는 LevelDB에 저장합니다.
머클 패트리샤 트리를 사용하는 가장 큰 이유는 어카운트가 새로 추가되고, 잔고나 스토리지가 빈번히 업데이트되는 상황으로 인하여, 정보를 효율적으로 저장, 수정, 삭제, 검색 등을 할 수 있는 데이터 구조가 필요하기 때문입니다. 머클 패트리샤 트리는 정보를 저장할 때 공통의 키 값을 따라 저장하므로 중복 저장을 하지 않아 공간이 절약됩니다.
또한, 트리 전체 정보를 뒤질 필요 없이 연결된 키 값들을 마치 path를 따라가는 것처럼 수정, 삭제, 검색을 하니 처리 속도가 빠릅니다. 추가적으로 트리의 깊이를 한정 짓는 것인데, 디도스 공격으로 인해서 트리의 깊이를 무한정 깊게 만들어서 성능 저하를 일으키는 것을 방지하기 위함입니다.
그림을 보면, 이더리움에서는 상태 머클 패트리샤 트리는 하나의 단일한 트리를 구성해서 각 블록 간에 사용되는 것을 확인할 수 있습니다.
머클 패트리샤 트리
다음 2개의 슬라이드에서는 머클 패트리샤 트리에 대한 주요 특징과 예시에 대해서 설명하겠습니다. 이더리움에서 사용하는 머클 패트리샤 트리의 주요 특징들에 대해서 설명하겠습니다. 첫째, 머클 패트리샤 트리에 있는 모든 항목들은 Recursive Length Prefix (RLP) 로 인코딩됩니다.
둘째, 머클 패트리샤 트리에 있는 모든 노드에 대한 경로들은 RLP 인코딩과 Keccak256 해싱을 거쳐 레벨 DB에 저장됩니다. 실제로 언급한 노드의 해시값은 레벨 DB에서 key로 저장되어 사용됩니다. Key로 조회하면, 해당 key에 대한 value를 이용해서 실제 node의 내용을 살펴볼 수 있습니다.
실제 노드에는 또 다음 노드에 대한 해시값이 있을 것이고, 이 과정을 반복하면 결국 마지막 노드에 저장된 값에 도달할 수 있습니다. 셋째, 트리의 노드는 성능 향상을 위해서 총 4가지 타입을 갖습니다. 각각은 공백 노드, 리프 노드. 확장 노드, 브랜치 노드입니다.
공백 노드는 비어있는 노드를 말합니다. 리프 노드는 [RLP 인코딩된 경로, 값]으로 이루어져 있고, 더 이상 내려가지 않는 노드를 의미합니다. 여기서 값은 이더(Ehter)와 같은 실제 값을 말합니다. 확장 노드는 [RLP 인코딩된 경로, 키] 목록으로 구성됩니다. 여기서 키는 연결된 노드의 해시값이고 levelDB 호출 시 키 값으로 사용합니다.
브랜치 노드는 [0, 1, 2,…,e, f, 값]으로, 17개 항목으로 구성된 리스트 구조입니다. 리스트 중 처음 16항목은 16진수 문자 값으로 다음의 노드를 가리키는 키 역할을 합니다. 이로 인해서 총 16개의 자식 노드를 가질 수 있습니다. 추가적으로 이 브랜치 노드의 마지막 항목이 [키, 값]을 갖는다면 17번째 값 항목에는 해당 [키, 값] 중 [값]만 저장합니다.
키는 이미 브랜치 노드까지 오면서 식별되었기 때문입니다. 마지막 특징으로서, 리프 노드와 확장 노드를 구별하기 위해서 선행구별자(prefix)를 사용하는 것입니다. 여기서 선행구분자는 hex-prefix 방식의 인코딩을 한 구분자입니다.
예를 들어서 아래의 표를 보면 선행구분자가 0이면 확장 노드이고 경로 값이 짝수라는 것을 말하는데, 1이면 경로 값의 길이가 홀수임을 나타냅니다. 만약 선행구분자가 2일 경우에는 리프 노드이고 경로 값의 길이가 짝수라는 것을 의미하며, 3일 경우 리프노드이지만 경로 값이 홀수라는 것을 의미합니다.
모든 경로 데이터는 byte 타입으로 저장되기 때문에 값을 구별하지 못하는 상황이 발생할 수 있는데, 예를 들어서 1과 01은 둘 다 동일하게 01로 저장되기 때문에 선행구분자 없이는 그 차이를 구분할 수 없습니다. 이때, 선행구분자를 두어서 1과 01의 차이를 구별할 수 있습니다.
다음 그림은 머클 패트리샤 상태 트리를 쉽게 이해하기 위해서 정리한 것입니다. 상태 정보의 경우에는 이더리움 블록체인 내에서 단일 공통의 상태 트리를 구성하여 사용하기 때문에 World State Tree 부르기도 합니다. Simplified World state는 value들에 단지 계정이 보유하는 이더리움 잔액 만을 나타내는 간소화된 버전입니다.
계정에는 이뿐만 아니라 다른 정보들도 있기 때문에 실제로는 계정 안에 있는 정보들[nonce, balance, storageRoot, codeHash] 배열을 RLP 인코딩한 값이 됩니다. 아무튼, 1시 방향의 간소화된 월드 상태(Simplified World state) 테이블을 보면, Key와 Value들을 보실 수 있습니다. Keys 값은 경로를 나타내며, 이 키 값을 따라가면 머클 패트리샤 트리의 경로를 따라서 리프 노드에 저장되어 있는 이더 잔액을 조회할 수 있습니다.
그림에 있는 Simplified World state의 key 값이 a77d337인 것을 lookup 한다고 가정을 하겠습니다. 루트(root) 노드에서 앞의 a7을 매치시켜 다음 노드의 해시값을 얻고, 이것을 key로 levelDB를 검색하여 브랜치 노드(Branch Node)를 얻어옵니다. 그리고 해당 브랜치 노드의 7번 인덱스에 있는 다음 노드의 해시값을 이용해서 다시 확장 노드(Extension Node)를 얻어옵니다.
여기까지 공유된 Hex 문자를 연결해보면 a77이고, 확장 노드의 공유 니블인 d3을 매치시켜서 다음 브랜치 노드의 해시를 얻어옵니다. 이를 이용해서 브랜치 노드를 얻어오고, 3번 인덱스에 있는 다음 노드의 해시를 이용해서, 리프노드(Leaf Node)를 얻어옵니다.
마지막 Hex가 7로 끝나면 value 값이 1.00WEI인 것을 확인할 수 있고, 이를 얻으면 lookup 프로세스가 끝나는 것입니다. 추가적으로 마지막 리프노드의 prefix가 3인 것은 키 값(경로 길이)이 홀 수이기 때문입니다.
참조
http://www.kmooc.kr/courses/course-v1:POSTECHk+CSED490U1+2021_T1/about
'SW > 블록체인' 카테고리의 다른 글
블록체인 : 이더리움 합의 알고리즘인 PoW 원리, 문제점 (0) | 2022.02.03 |
---|---|
블록체인 : 이더리움 네트워크에서 사용되는 암호화폐인 이더와 토큰인 Gas : 이더리움의 전반적인 트랜잭션 처리 과정 (0) | 2022.02.02 |
블록체인 : 이더리움의 데이터 계층 : 어카운트, 트랜잭션, 트랜잭션 리시트의 구성 요소 (0) | 2022.01.31 |
블록체인 : 이더리움의 탄생, 로드맵 : 비트코인과의 차이점 (0) | 2022.01.30 |
블록체인 : 비트코인 마이닝 알고리즘 원리, 이해 : 네트워크 분기 합의 알고리즘 원리, 이해 (0) | 2022.01.29 |