블록체인에서 블록이 무엇인지에 대해서 정리하고자 합니다.

블록은 다수의 거래정보와 블록헤더로 구성되어 있습니다.

블록체인이란 말그대로 이러한 블록들이 연속적으로 연결되어 있음을 의미합니다.

그럼 이와같은 블록이 어떤정보로 구성되어있는 보도록 하겠습니다.


비트코인의 블럭정보는 https://blockchain.info/ 에서 확인이 가능합니다.

가장 최신블럭이 다음과 같이 보여지고 있으며, 여기서 관심있게 봐야할 부분인 Age를 보면 평균적으로 블록간의 시간이 10분정도 소요됩니다.

이말은 비트코인의 블록은 평균 10분마다 생성이 되며, 10분간의 거래가 해당블럭에 포함이 된다는 말입니다.



그럼 511435번 블럭을 살펴보도록 하겠습니다.

이 블록은 말그대로 511435번째로 생성된 블럭을 말하며, 0번 블록은 비트코인이 최초에 실행될때 만들어진 제네시스(Genesis) 블록이 됩니다.

각 블록간의 생성시간은 위에 언급한것처럼 평균 10분이 소요됩니다.


블록은 헤더와 트랜잭션(Transaction)으로 구성되며, 블록헤더는 다음과 같이 구성되어 있습니다. (Block.h)


hashPrevBlock과 hashMerkleRoot의 데이타타입이 uint256으로 구성되어있음을 볼수있는데, 이 값들은 모두 SHA256 알고리즘을 통해 생성되는 값이기 때문에 256비트값을 가지게 됨을 짐작할수 있습니다.



Timestamp(블록이 생성된 시간) : 2018-03-01 08:11:49 

Bits(블록 생성의 난이도) : 392009692

Version(프로토콜 버전) : 0x20000000

Merkle Root(트랜잭션 정보를 머클트리 형태로 구성한 해시정보) : 

   b958fda3ce4d3cd0a41f01a6b00f6ae4e70f498a27f526cc5612f72450152d17

Previois Block(이전 블록의 해시정보) : 

   00000000000000000029b633899beed322666cc0041fbdf471300d52e6de1887

Nonce : 2170187189


Hash : 0000000000000000003265329296e10cc51cc37cb0fb8f31636bb15d9fdf8a60


블록헤더과 트랜잭션은 다음과 같이 구성됩니다.



여기서 블록헤더를 구성하는 6개값중 아래의 5개는 그 값이 고정된 상수입니다.

(version, previous hash, merkle root, timestamp, bits)

이 5개의 정보와 Nonce 값을 사용하여 SHA256 알고리즘을 통해 해당 블록의 Hash 값이 계산됩니다.

이 Hash 값이 Bits로 지정된 난이도값보다 작게 생성될때 해당블록이 생성에 성공이 되며, 이 과정이 채굴(Mining) 입니다.

채굴알고리즘은 이후에 다시 정리할려고 합니다.


이와같이 하나의 블록은 계산된 Hash 값과 블록헤더, 10분간 진행된 거래(Transaction) 내역이 포함되며 이 블록이 다음과 같이 연속된 체인형태로 구성되게 됩니다.

이렇게 블록들이 연속적인 체인형태로 구성이 되었다고 해서 블록체인이라고 하는것입니다.




'프로그래밍 & IT > BlockChain' 카테고리의 다른 글

ubuntu에 geth 설치  (0) 2018.08.28
블록체인에서의 거래  (0) 2018.08.25
비밀키와 공개키  (0) 2018.08.20
블록체인의 기본, 해시란 무엇인가?  (0) 2018.08.20
블록체인 한번에 이해하기  (0) 2018.08.20

그래프 관련 문제를 풀 때는, 문제 상황을 그래프로 모델링한 후에 푸는 것이 보편적입니다. 이 때, 모델링한 그래프의 연결관계를 나타내는 두 가지 방식이 있습니다.


1. 인접 행렬

2. 인접 리스트



1. 인접 행렬


1) 정의

인접 행렬은 그래프의 연결 관계를 이차원 배열로 나타내는 방식입니다. 인접 행렬을 adj[][]1라고 한다면 adj[i][j]에 대해서 다음과 같이 정의할 수 있습니다.

 

adj[i][j] : 노드 i에서 노드 j로 가는 간선이 있으면 1, 아니면 0


cf) 만약 간선에 가중치가 있는 그래프라면 1 대신에 가중치의 값을 직접 넣어주는 방식으로 구현할 수 있습니다.


2) 예시

간단한 예시를 통해 이해해보도록 하겠습니다. 다음과 같은 그래프가 있는 경우, 이 그래프의 연결 관계를 인접 행렬로 나타낸다면 인접 행렬은 다음과 같을 것입니다.






그렇다면, 위의 그래프처럼 간선에 방향이 있는 유향 그래프가 아닌, 간선에 방향이 없는 무향 그래프의 경우에는 어떻게 될까요?

노드 i에서 노드 j로 가는 간선이 있다는 말은 노드 j에서 노드 i로 가는 간선도 존재한다는 의미가 되겠죠? 따라서, 인접 행렬이 대각 성분(adj[i][j]에서 i와 j가 같은 원소들)을 기준으로 대칭인 성질을 갖게 됩니다. 위의 그래프의 간선들을 방향이 없는 간선으로 바꿔주면 아래와 같은 인접행렬을 갖게 됩니다.




3) 장단점

인접 행렬의 장점은 구현이 쉽다는 점입니다. 

그리고, 노드 i와 노드 j가 연결되어 있는지 확인하고 싶을 때, adj[i][j]가 1인지 0인지만 확인하면 되기 때문에 O(1)이라는 시간 복잡도에 확인할 수 있다는 점이 있습니다. 

하지만, 치명적인 단점 또한 존재합니다. 전체 노드의 개수를 V개, 간선의 개수를 E개라고 해봅시다. 노드 i에 연결된 모든 노드들에 방문해보고 싶은 경우 adj[i][1]부터 adj[i][V]를 모두 확인해보아야 하기 때문에 총 O(V)의 시간이 걸린다는 점입니다. 위의 그래프와 같은 경우 큰 차이가 느껴지지 않을 수도 있지만, 노드의 개수에 비해 간선의 개수가 훨씬 적은 그래프라면 이야기가 달라질 것입니다. 만약, 노드의 개수는 총 1억개인데 각 노드마다 연결된 간선이 많아봤자 2개인 그래프가 있다고 해봅시다. 그렇다면, 특정 노드와 연결된 노드들이 몇 번 노드인지 확인하기 위해 총 1억 개의 노드들을 모두 확인해봐야 하는 치명적인 문제가 발생하게 됩니다. 정작 연결된 노드는 많아봤자 2개 뿐일텐데 말입니다. (이러한 단점을 보완할 수 있는 연결 관계 표현 방식이 인접 리스트입니다.)


4) 구현

문제를 풀 때엔 그래프에 대한 정보가 


① 노드의 개수

② 간선의 개수

③ 각 간선의 양끝 노드


의 순서로 주어지게 됩니다. 위의 무향 그래프를 입력으로 주고 싶다면, 문제에선 어떤 식으로 정보를 제공하게 될까요?


5

1 2

1 3

1 4

2 3

3 4


다음과 같이 제공하게 되겠죠?? 그렇다면 이를 인접행렬로 저장하기 위해선, 어떻게 코드를 작성해야 할까요? 

노드의 개수와 간선의 개수를 입력받은 후, 간선의 개수만큼 for문을 돌면서 양끝 노드를 입력받으면 될 것입니다. 자세한 구현은 아래와 같습니다.


정답 코드 보기



2. 인접 리스트


인접 리스트를 구현하기 위해서는 STL의 vector를 사용하는 것이 편합니다. 아직 사용법을 모르신다면, 간단한 사용법을 익혀두는 것이 좋습니다. (vector 사용법 : http://sarah950716.tistory.com/4)


1) 정의

인접 리스트는 그래프의 연결 관계를 vector의 배열(vector<int> adj[])로 나타내는 방식입니다. 이 때, vector<int>에는 노드의 번호가 직접 저장됩니다. 인접 리스트는 adj[i](vector의 배열이기 때문에, adj[i]는 vector가 되겠죠?)를 다음과 같이 정의할 수 있습니다.


adj[i] : 노드 i에 연결된 노드들을 원소로 갖는 vector


cf) 만약 간선에 가중치가 있다면, pair<int,int> adj[]를 통해 구현할 수 있습니다. 

    pair<int,int>의 first에는 노드의 번호, second에는 간선의 가중치를 저장하는 방식으로 저장해주면 됩니다.


2) 예시

이번에도 역시 간단한 예시를 통해 이해해보도록 하겠습니다. 




위의 그림에서 편의상, 벡터를 링크드 리스트와 같은 형태로 나타내었고 (vector는 push_back을 통해 뒤에 원소를 계속 붙일 수 있기 때문에 링크드 리스트의  형태와 비슷하다고 생각하면 편하기 때문) , 보기 좋도록 adj[]를 세워서 나타냈습니다. 왼쪽의 그래프를 보면, 노드 1과 연결된 노드는 노드 2, 3, 4. 총 세 개가 있습니다. 따라서, adj[1]의 vector는 총 세 개의 성분을 갖게 됩니다. 각각의 성분에 접근하기 위해선 이차원 배열과 같은 방식을 이용하면 됩니다. (adj[1][0] = 2, adj[1][1] = 3, adj[1][2] = 4, adj[2][0] = 3) 


cf ) 오른쪽의 인접 리스트에서, adj[1]에 있는 세 노드의 순서 의미가 없습니다. 2,3,4 순이 아닌 3,2,4 순이 되어도 무관하지만, 보기 좋도록 하기 위해 오름차순으로 저장해 놓은 것을 뿐입니다. 따라서, adj[1]의 노드 2와 노드 3사이에 화살표가 있는 것을 노드 2와 노드 3사이에 간선이 있다는 의미가 아니라 단순히, vector에 노드 3이 노드 2보다 나중에 push_back 되었기 때문이라고 이해하면 됩니다.


그렇다면, 간선에 방향이 없는 무향 그래프의 경우 인접 리스트가 어떻게 될까요?

그림으로 간단하게 이해하고 넘어가도록 하겠습니다.



 


3) 장단점

그렇다면, 인접 리스트로 그래프의 연결 관계를 저장할 때의 장점은 무엇일까요? 인접 리스트는 인접 행렬과 달리, 실제로 연결된 노드들에 대한 정보만 저장하기 때문에, 모든 벡터들의 원소의 개수의 합이 간선의 개수와 같다는 점이 있습니다. 즉, 간선의 개수에 비례하는 메모리만 차지한다고 할 수 있겠죠? 위의 그림만 봐도 이러한 점을 확인할 수 있습니다. 그래프에 5개의 간선이 존재하고, 따라서 인접 리스트 상에 저장된 원소도 5개인 것을 확인할 수 있습니다.

만약, 노드 2와 연결된 모든 노드들을 방문해보고 싶다면, 인접 행렬의 경우 adj[2][1], adj[2][2], adj[2][3], adj[2][4]. 총 4번을 확인해보아야 하지만, 인접 리스트의 경우 실제 연결된 노드들만 확인해 볼 수 있기 때문에 adj[2][0] 부터 adj[2][adj[2].size()-1] 까지 총 1번만 확인해보면 됩니다. 

이를 전체 노드에 대한 탐색을 수행하는 상황으로 확장해서 생각해보도록 하겠습니다. 노드가 V개, 간선이 E개라고 해봅시다. 인접 행렬의 경우,  각 노드에 연결된 노드를 방문해보기 위해서 모든 노드에 대해 확인해보아야 하기 때문에 총 O(V*V)의 시간이 걸릴 것입니다. 하지만, 인접 리스트의 경우에는 각 노드마다 연결된 노드만 확인하는 것이 가능하기 때문에, 전체 간선의 개수만큼만 확인해 볼 수가 있습니다. 따라서, O(E)의 시간복잡도를 가진다고 할 수 있겠죠? 이렇게 각 노드에 연결된 모든 노드들을 방문해 보아야 하는 경우, 인접 리스트로 연결 관계를 저장하는 것이 시간상 큰 이점을 가진다고 할 수 있습니다. 

하지만, 인접 리스트에도 치명적인 단점이 존재합니다. 노드 i와 노드 j가 연결되어 있는지 알고 싶다면 어떻게 해야 할까요? adj[i]의 벡터 전체를 돌며, j를 성분으로 갖는지 확인해보아야 하겠죠? 따라서, 시간 복잡도는 O(V)가 될 것입니다. 위에서 언급했듯이, 인접 행렬의 경우 adj[i][j]가 1인지 0인지만 확인하면 되기 때문에 O(1)의 시간 복잡도를 갖게 되지만 말입니다. 이러한 단점이 있기 때문에, 문제의 상황에 따라 적절한 표현 방식을 이용해서 연결 관계를 저장하는 것이 중요하다고 할 수 있습니다.


4) 구현

간선에 가중치가 없는 무향 그래프를 인접 리스트로 저장하는 방식을 구현하는 코드를 통해, 그래프의 연결 관계 저장 방법에 대한 공부를 마무리 하도록 하겠습니다.


정답 코드 보기



+예시)

간선에 가중치가 있는 그래프를 인접리스트로 나타내는 경우, vector<pair<int,int>>의 배열로 구현하게 됩니다.





adj[a][0].first = b, adj[a][0].second = c 라고 하면,

시점이 노드 a, 종점이 노드 b, 가중치가 c인 간선이 존재한다는 것을 의미합니다.


  1. 인접 행렬을 영어로 하면 adjacent matrix이기 때문에 주로 adj[][]라고 변수명을 줄여 말합니다. [본문으로]



출처: http://sarah950716.tistory.com/12 [알고리즘 정복기]

비밀키와 공개키의 원리


지난 장에서는 상당수 블록체인(Block Chain)의 핵심 알고리즘인 SHA-256 해시(Hash)에 대해서 공부하는 시간을 가졌다. 블록체인은 근본적으로 해시 알고리즘을 이용함으로써 코인의 거래 장부가 정상임을 보장할 수 있다. 즉 거래 장부의 무결성(Integrity)을 보장할 수 있는 것이다. 다만 ‘거래 기록이 변조되지 않았음’을 보장할 수는 있으나 이것만으로는 장부 안에 있는 모든 기록이 애초에 사실이었는지는 보장할 수 없다. 만약 과거의 거래 기록 중에서 ‘A가 B에게 100코인을 보냈다’라는 기록이 존재한다고 할 때 이 기록 자체가 거짓된 것이라면 어떻게 할까?


[그림 1] 거짓 장부를 작성하는 기본 아이디어


간단한 예시를 살펴보자. 가장 대표적인 거짓 장부의 예는 실제로 보내지도 않은 돈을 보냈다고 속이는 것이다. 특정 블록체인에서 위와 같이 ‘동빈’이라는 나쁜 채굴 참여자가 ‘종구’가 자신에게 100 코인을 전송했다는 거짓 거래 내역을 장부에 기록함으로써 자신이 종구의 코인을 가지고 있다고 주장하고 있다. 지난 절에서 자세히 배웠듯이 장부에 기록된 정보는 해시(Hash)에 의해서 무결성을 보장받으므로 블록이 연달아 생성될수록 더욱 변조하기가 어렵다. 따라서 애초에 ‘거짓 거래 내역’이 공공 거래 장부에 기록되는 순간 이를 되돌리기가 어렵다는 것이다.


그러므로 애초에 ‘동빈’이 ‘종구’의 이름으로 거래 내역을 기록할 수 없도록 해야 한다. ‘종구’가 코인을 전송했다는 정보는 반드시 ‘종구’만 기입할 수 있어야 한다. 실제로 블록체인에서는 이것을 공개키 기반 구조(Public Key Infrastructure)를 이용해 해결한다. 공개키 기반 구조란 비밀키와 공개키라는 두 개의 키를 이용하는 암호 구조이다. 이러한 공개키 기반 구조에서 특정한 데이터를 암호화하고 싶을 때는 먼저 비밀키를 이용해 그 데이터를 암호화한다. 이후에 암호화된 데이터는 오직 그 비밀키와 한 쌍을 이루는 공개키를 이용했을 때만 복호화할 수 있다.


이러한 특징 때문에 공개키 기반 구조는 주로 행위를 증명할 때 사용한다. 가령 A가 B에게 100코인을 전송했다는 사실을 증명하고자 하는 경우를 살펴보자. A는 자신이 가지고 있는 비밀키를 이용해 자신이 B에게 100코인을 전송했다는 메시지를 암호화한다. 이후에 그 암호문을 공개키와 함께 주변 네트워크에게 전달한다. 이후에 주변 네트워크에 포함되어 있는 사람들은 해당 암호문이 A의 공개키를 이용해 복호화를 했을 때 성공적으로 복호화가 되는 것을 보고, A가 B에게 코인을 전송했다는 사실을 확인한다. 실제로 코인 거래의 증명은 이러한 방식을 따른다.


[그림 2] 공개키 기반 구조를 활용한 코인 전송의 원리


그렇다면 여기에서 한 번 더 나아가서 특정한 공개키의 주인이 A라는 사실은 어떻게 증명할 수 있을지 알아보자. 이는 공개키의 주인을 식별할 때 사람의 이름이나 아이디를 사용하지 않고 지갑 주소(Address)를 사용함으로써 해결하는데, 여기에서 해시(Hash)의 개념이 한 번 더 등장한다. 실제로 우리가 코인을 거래할 때는 공개키에 해시 알고리즘을 적용한 문자열을 생성하여 이를 지갑 주소로 사용한다. 즉 실제로 블록체인에서 A라는 사람은 그 지갑 주소로서 대변되는 것이다. 이 때 지갑 주소는 오직 해당 공개키로만 만들 수 있으며 중복되지 않는다.


따라서 네트워크에 참여한 사람들은 특정한 암호 메시지를 공개키로 복호화를 한 뒤에 메시지에 적혀 있는 ‘코인을 전송하는 사람’이 해당 공개키에 해시 알고리즘을 적용한 값과 일치하는지 확인한다. 이 때 코인을 전송하는 사람, 즉 송신자 지갑 주소가 해당 공개키에 해시를 적용한 지갑 주소와 일치하는 경우에만 정상적인 거래로 인정해주는 것이다. 이와 같이 블록체인은 다양한 종류의 암호화 알고리즘이 덕지덕지 붙어 있는 암호화 기법의 집합체라고 할 수 있다.


그러므로 이러한 블록체인의 기술에 따라서 우리는 타인의 지갑에 있는 코인을 쉽게 빼낼 수 없다. 개인의 지갑 주소는 네트워크에 공유되므로 쉽게 알아낼 수 있지만 ‘특정 지갑 주소로 다른 지갑으로 코인을 전송했다’는 메시지를 만들기 위해서는 특정 지갑의 비밀키를 알아내야 하기 때문이다. 결과적으로 개인키를 도난당하지 않는 이상 자신의 코인은 안전하다고 볼 수 있다. 구체적으로 개인키부터 공개키와 지갑 주소를 생성할 때 사용하는 매커니즘은 다음과 같다.


[그림 3] 블록체인의 지갑 주소 생성 매커니즘


공개키 기반 구조를 근간으로 하는 암호화 알고리즘에는 다양한 종류가 있지만 블록체인에는 ‘타원 곡선 암호 알고리즘(ECC)’이 주로 사용된다. 타원 곡선 암호 알고리즘은 쉽게 말해 ‘특정한 비밀키를 입력했을 때 타원 곡선을 이용해 그에 상응하는 공개키를 생성해주는 알고리즘’이다. 이 또한 당연히 공개키 기반 구조이므로 공개키를 가지고 원해의 비밀키가 무엇이었는지 알 수 없다는 점에서 강력하다. 공개키를 생성한 이후에는 해시 알고리즘을 적용해 지갑 주소를 생성한다. 이 때 일반적으로 이전 시간에 배웠던 SHA-256 알고리즘을 이용한다.


호기심이 많은 독자라면 자신이 직접 비밀키와 공개키를 생성을 해보고 싶을 것이다. 다행히도 이미 인터넷에는 수없이 많은 자료가 존재하기 때문에 누구나 쉽게 위 매커니즘을 실습해 볼 수 있다. 다음에서 블록체인 암호화를 실습하는 시간을 가져볼 것이다.


공개키를 이용한 블록체인 암호화 실습하기


공개키를 이용한 블록체인 암호화 기법에 대해서 자세히 이해하기 위해서는 실습을 통해 직접 체험해보는 것이 좋다. 앤더스 브라운워스(Anders Brownworth)가 개발한 무료 교육용 웹 사이트인 블록체인 데모(Blockchain Demo)를 이용해보자. 전체 웹 사이트 소스코드는 오픈소스로 공개되어 있다. URL(https://anders.com/blockchain/public-private-keys/keys.html)에 접속하면 다음과 같은 화면을 만날 수 있다.



사이트에 처음 접속하면 위와 같이 무작위의(Random) 비밀키(Private Key)가 생성되어 있다. 또한 해당 비밀키를 이용해 만들어진 공개키(Publick Key)가 출력된다. 사용자가 임의대로 비밀키를 변경할 수 있는데 변경을 할 때마다 공개키도 자동으로 변경된다. 실제 블록체인에서도 비밀키는 절대 잊어버리거나 누설해서는 안 되는 중요한 정보이다.



이제 위와 같이 ‘Signatures’ 탭으로 이동하면 위와 같이 특정한 메시지를 비밀키를 이용해 암호화할 수 있다. 암호문은 메시지 서명(Message Signature)로 표현된다. 이제 이후에 이 암호화된 메시지와 공개키를 주변 네트워크로 전달했다고 가정해보자. 



이후에 ‘Verify’ 탭으로 이동한 뒤에 ‘Verify’ 버튼을 누르면 해당 암호문 메시지를 공개키로 복호화 하여 성공적으로 데이터가 검증되었다고 출력되는 것을 알 수 있다. 이는 블록체인 네트워크에서 주변 컴퓨터들이 특정한 하나의 거래에 대해서 ‘입증’을 해주는 과정과 같다.



이후에 ‘Transaction’ 탭으로 이동한 뒤에 다음과 같이 다시 한 번 서명을 생성해보자.



이번에는 위 그림에서 보여주는 바와 같이 단순한 문자열 메시지가 아니라 실제로 자신의 지갑에서 특정한 지갑으로 코인을 전송했다는 데이터를 토대로 메시지 서명을 만들어낸 것을 알 수 있다. 물론 내부적인 작동 원리는 직전에 했던 단순 문자열 메시지 서명과 거의 동일하다.



이후에 ‘Verify’ 탭으로 이동하면 실제로 주변 네트워크가 해당 메시지 서명을 복호화하는 과정을 테스트할 수 있다. 다만 이 탭에서는 복호화에 필요한 공개키가 존재하지 않는 것을 알 수 있다. 예리한 독자들은 이미 눈치를 챘겠지만 공개키는 지갑의 주소와 1:1로 대응한다. 따라서 송신자(From)에 적혀있는 문자열이 바로 공개키 자체로 사용되는 것이다. 이 블록체인 데모 사이트는 말 그대로 블록체인 데모이므로 블록체인의 핵심 원리를 설명하는 데에 주력하고 있다. 실제로 암호화폐의 블록체인을 그대로 가져온 것은 아니므로 이론적으로만 이해하면 충분하다.



마지막으로 이전 시간에 배운 블록체인의 무결성에 대한 내용과 공개키 기반 구조에 대한 내용을 모두 종합한 예시를 확인해보자. 위와 같이 마지막 탭인 ‘Blockchain’ 탭으로 이동하면 여러 개의 블록이 존재하고, 블록마다 여러 개의 거래 내역이 포함되어 있다. 또한 각 거래 내역은 공개키 알고리즘이 적용되어 있다. 각 메시지 서명(Signature)들은 블록의 ‘현재 해시’ 값에 영향을 미친다는 점에서 특정한 거래 내역의 데이터를 조금만 변경해도 해당 블록은 잘못된 블록으로 인식된다. 이와 같이 블록체인은 공개키 기반 구조와 해시 알고리즘이 적절히 어우러져 있다.

블록체인이 작동하는 원리를 제대로 이해하기 위해서는 무결성에 대한 이해가 필요하다. 무결성(Data Integrity)은 ‘특정한 데이터를 보호하여 그 데이터를 정상인 상태로 유지하는 성질’을 의미한다. 무결성은 정보보안 영역에서 굉장히 중요한 키워드다. 블록체인은 암호화폐, 투표 시스템 등에 적용이 된다는 점에서 무결성을 보장하지 못하면 사람들이 신뢰하고 사용하지 않을 것이다. 즉 누구도 데이터를 변질시킬 수 없는 특성, 무결성이 있어 암호화폐도 존재하는 것이다. 먼저 정보보안에서 가장 중요시하는 세 가지 요소인 CIA에 대해 알아보자.



[그림 1] 정보보안의 세 가지 요소(CIA)


CIA란 기밀성(Confidentiality), 무결성(Integrity), 가용성(Availability)의 앞 글자를 딴 단어다. 각종 IT 관련 자격증 시험에서도 매 번 나오는 개념이므로 컴퓨터에 관심이 많은 독자라면 이를 기억해두면 좋을 것이다. 정보보안에서는 무결성 뿐만 아니라 기밀성, 가용성이라는 요소를 중요하게 여기고 시스템을 설계할 때 이를 기준으로 삼는다. 기밀성이란 ‘허가된 자만 데이터에 접근할 수 있도록 하는 것’이고, 가용성은 ‘서비스가 원활하게 제공되도록 하는 것’이다.


예를 들면 개발자가 웹 서비스를 개발할 때는 사용자가 회원가입 시에 비밀번호를 설정하도록 한다. 일반적으로 타인의 비밀번호를 모르면 접근할 수 없으므로 기밀성을 보장하는 것이다. 또한 비밀번호를 데이터베이스에 저장할 때 해시(Hash)라는 알고리즘을 적용해 해커가 이를 취득해도 원래의 비밀번호를 알아낼 수 없도록 한다. 이는 데이터가 정상임을 보장해주는 해시의 개념을 적용했다는 점에서 무결성을 지킨 것이다. 마지막으로 DDoS 공격 등으로부터 서버를 보호해 웹 서비스가 항상 동작 가능하도록 한다. 이는 가용성을 지키는 것이라고 할 수 있다.


암호화폐의 블록체인은 기본적으로 동일한 장부 데이터를 전 세계의 많은 컴퓨터들이 공유하고 가지고 있다는 점에서 가용성을 철저하게 보장받을 수 있다. 장부가 한 컴퓨터에만 담겨있지 않다는 점에서 해커들은 DDoS 공격을 수행할 대상이 되는 단일 서버를 설정할 수 없기 때문이다. 또한 각 개인의 지갑은 고유한 개인키를 가지고 있어 자신이 아닌 다른 사람이 나의 암호화폐를 인출할 수 없어 기밀성을 보장한다. 마지막으로 블록체인은 누구도 장부 내용을 변조할 수 없도록 해시(Hash) 알고리즘이 시스템 전반에 적용되어 무결성을 보장한다.


사실 모든 사람이 동일한 장부 데이터를 가진다는 블록체인 아이디어를 생각해보면 기밀성과 가용성이 쉽게 지켜질 것이라는 점은 이해하기 어렵지 않다. 하지만 장부의 내용을 누구도 쉽게 변조할 수 없으며 장부를 변조했다고 하더라도 다른 사람들이 어떻게 그 사실을 바로 알아차릴 수 있는 것인지, 그 무결성 매커니즘은 잘 이해가 가지 않을 것이다. 무결성이 성립 가능한 이유를 이해하기 위해서는 약간의 수학적 사고가 필요하다. 바로 컴퓨터공학부의 전공과목인 자료구조(Data Structure)나 암호학 관련 강의에서 자주 배우게 되는 해시(Hash) 알고리즘이다.


해시(Hash)의 정의


일반적으로 우리가 특정한 정품 소프트웨어를 다운로드 받을 때 그 소프트웨어가 해커로 인해 변질되지 않은 정상적인 소프트웨어인 것을 어떻게 검증할 수 있을까? 우리는 이 때 해시(Hash)라는 것을 이용하여 검증한다. 이전 강좌에서도 간단히 언급을 했는데 해시는 ‘특정한 데이터를 이를 상징하는 더 짧은 길이의 데이터로 변환하는 행위’를 의미한다. 여기에서 상징 데이터는 원래의 데이터가 조금만 달라져도 확연하게 달라지는 특성을 가지고 있어 무결성을 지키는 데에 많은 도움을 준다. 예를 들어 ‘A’라는 문자열의 해시와 ‘B’라는 문자열의 해시는 고작 한 알파벳이 다를 뿐인데 그 결과가 천차만별이라는 것이다. 예를 들어 대표적인 해시 알고리즘인 MD5는 어떠한 입력 값을 넣든 항상 각양각색의 32자리의 16진수 값을 반환한다.



[그림 2] 대표적인 해시 알고리즘인 MD5의 연산 결과


16진수는 ‘1부터 9까지의 숫자와 A부터 F까지의 알파벳으로 수를 표현하는 방식’이다. 실제로 ‘A’라는 문자열의 MD5 해시 결과 값은 ‘7FC56270E7A70FA81A5935B72EACBE29’이라는 16진수 문자열을 가진다. 반면에 알파벳 하나만 다른 ‘B’라는 문자열의 MD5 해시 결과 값은 완전히 다른 문자열인 ‘9D5ED678FE57BCCA610140957AFAB571’다. 해시는 그 결과 값을 완전히 바꾸어준다는 점에서 강력한 도구다. 이렇게 ‘암호화된 정보가 큰 폭으로 변화하는 효과’를 눈사태 효과(Avalanche Effect)라고 하는데 좋은 암호 알고리즘들은 모두 눈사태 효과를 그 특성으로 가진다. 흔히 소프트웨어 판매 업체에서는 판매하는 소프트웨어 설치 프로그램의 바이너리 코드에 해시 알고리즘을 적용한 결과 값을 웹 사이트에 함께 게시한다. 고객들은 자신이 어디선가 받은 소프트웨어가 정품인지 확신이 안 설 때 자신이 가지고 있는 소프트웨어의 해시 값이 사이트에 출력된 해시 값과 일치하는 지 확인하여 검증할 수 있다.


MD5 해시 알고리즘의 경우 온라인 MD5 해시 생성기 웹 사이트에 접속하여 실습해볼 수 있다. 해당 URL(http://www.convertstring.com/ko/Hash/MD5)로 접속해 몇 가지 입력 데이터를 넣어 MD5 해시를 거친 결과 데이터가 나오는 것을 확인해보자.


비둘기집의 원리


대부분의 해시 알고리즘들은 항상 고정된 길이의 결과 문자열을 반환한다는 특징을 가지고 있다. 앞서 언급한 MD5 해시 알고리즘은 128 비트로 구성된 결과, 즉 32자리의 16진수 값을 반환한다. 이 문자열의 길이는 변함이 없다. 그렇다면 128 비트라는 결과는 이론적으로 최대 몇 개의 경우의 수를 가질 수 있을까? 한 비트(Bit) 단위는 0 혹은 1이라는 두 가지 경우의 수를 가진다는 점에서 128bit는 총 2의 128 제곱만큼의 경우의 수를 표현할 수 있다. 이는 약 340 간(澗)에 해당하는 매우 큰 숫자이다. 단순히 무차별 대입(Brute-Forcing)으로 해시 결과가 동일한 두 입력 값을 찾기 위해서는 개인용 컴퓨터로는 억겁의 시간이 소요될 것이다.



[그림 3] 128비트로 표현할 수 있는 경우의 수 근사치


하지만 아무리 큰 숫자라고 하더라도 무한(Infinite)은 아니라는 점에서 특정한 두 입력 값의 결과 해시 값이 동일한 경우가 발생할 수 있다. 예를 들어 입력 값의 개수가 2의 128 제곱을 넘어가게 되면 최소한 한 쌍의 입력 값은 그 결과 값이 동일할 것이다. 더 간단한 예로 설명을 하자면 비둘기가 5마리일 때 상자가 4개밖에 존재하지 않는다면 아무리 비둘기를 균등하게 분배해도 최소한 한 상자에는 2마리의 비둘기가 들어가게 된다. 지난 시간에도 언급했지만 이처럼 ‘n + 1개의 물건을 n개의 상자에 넣을 때 최소한 하나의 상자에는 두 개 이상의 물건이 들어 있는 원리’를 비둘기집 원리라고 말한다. 당연히 해시는 수학적 공리인 비둘기집 원리를 따른다.



[그림 4] 비둘기집 원리 예시


비둘기집 원리에 따라 해시에서는 이처럼 ‘서로 다른 입력 값의 해시 결과 값이 동일한 문제’, 즉 한 상자에 두 개의 물건이 들어가는 현상인 충돌(Collision)이 발생할 여지가 있다. 당연히 충돌이 적게 발생할수록 좋은 해시 알고리즘이다. 또한 만약 특정한 해시 알고리즘에 대하여 무차별 대입이 아닌 수학적인 연산 과정으로 원하는 문자열의 해시 결과 값을 만들어낼 수 있다면 그 해시 알고리즘은 무결성을 보장할 수 없다는 점에서 더 이상 사용되지 않게 될 것이다.


더불어 해시는 기본적으로 복호화가 불가능하다는 특징이 있다. 이는 당연히 입력 데이터 집합이 출력 데이터 집합을 포함하고 있으므로 특정한 출력 데이터를 토대로 입력 데이터를 찾을 수 없기 때문이다. 즉 동일한 출력 값을 만들어낼 수 있는 입력 값의 가짓수는 수학적으로 무한(Infinite)개라고 볼 수 있다. 만약에 해시 결과 값에 대해서 복호화를 수행할 수 있다면 이는 압축률이 무한인 압축 알고리즘과도 같다. 해시는 애초에 복호화를 수행할 수 없도록 설계 되었으며, 실제로도 해커가 쉽게 복호화를 할 수 없다는 점에서 매력적인 알고리즘인 것이다.


MD5와 SHA-1


이제 과거에 많이 사용되었던 대표적인 해시 알고리즘인 MD5와 SHA1에 대해서 자세히 알아보도록 하자. MD5(Message-Digest Algorithm 5)는 ‘128비트 고정 길이의 결과를 가지는 암호화 해시 함수’다. 대표적인 인터넷 표준안인 RFC 1321로 지정되어 있고, 10년이 넘는 시간동안 컴퓨터 시스템에 사용되어 해시 알고리즘의 아버지와도 같다. MD5는 최근 개인용 PC 한 대로 수 초 내로 해시 충돌을 찾을 수 있을 정도의 효율적인 공격 알고리즘이 발표되어 무결성을 보장하기 어렵다는 점에서 현재는 사용되지 않고 있다.


MD5의 핵심 알고리즘은 32비트 워드 네 개로 이루어진 128 비트 스테이트(Bit State)를 이용하여 연산하는 것이다. 처음에 각 워드는 A, B, C, D라는 변수명으로 상수 값을 가진다. 이후에 입력 문자열이 블록 단위로 들어오게 되면 각 워드를 이용해 연산 과정을 거치고, 그 결과로 128 비트 스테이트의 값을 새롭게 갱신한다. 입력 문자열 블록을 처리하는 과정은 총 4 라운드(Round)로 구성되는데 이산수학에서 사용되는 기초 연산을 이용하므로 관련 문서를 찾아보면서 구현하면 고급 프로그래머가 아니더라도 프로그래밍 언어로 쉽게 구현할 수 있다는 장점이 있다.


이러한 MD5는 현재 무결성을 효과적으로 보안하지 못한다는 점에서 이보다 더 나은 해시 알고리즘인 SHA-1 혹은 나아가 SHA-256을 사용하도록 권장되고 있다. SHA-1(Secure Hash Algorithm 1)은 ‘미국 국가안보국(NSA)에서 처음으로 개발한 해시 암호화 모델로 MD5를 대신하여 많이 사용되고 있는 해시 알고리즘’이다. 기본적으로 알고리즘은 MD5 해시 함수에서 사용되었던 것과 같이 다양한 기초 수학적 연산 과정을 포함한다. 이러한 SHA-1 알고리즘도 MD5 알고리즘만큼 치명적인 보안 결함이 발견된 것은 아니지만 충돌이 발견된 적이 있다는 점에서 보다 보안이 중요한 시스템에서는 이후에 다루게 될 SHA-256을 사용하도록 권장되고 있다.


기본적으로 충돌(Collision)은 해시 알고리즘의 이용 과정에서 충분히 발생할 수 있다. 단순히 수학적으로 계산해도 컴퓨터가 확률적으로 언젠가는 충돌을 찾아낼 수 있기 때문이다. 하지만 무차별적으로 데이터를 대입하는 방식인 무차별 대입(Brute-Forcing)이 아니라 충돌을 연산 수식을 통해 ‘의도적으로’ 발생시킬 수 있다면 보다 큰 문제가 될 수 있다. 앞서 다루었던 MD5 알고리즘은 이미 다양한 프로그램을 이용해 손쉽게 의도적으로 해시 충돌을 만들어낼 수 있다는 점에서 심각한 보안 결함을 가지고 있다. 이러한 측면에서 저자 또한 실제로 웹 서비스 등의 프로그램을 개발할 때 고객의 비밀번호를 반드시 SHA-256 이상의 해시를 적용해 저장한다.


레인보우 테이블 공격


앞서 해시는 복호화가 불가능하다는 특징이 있다고 했는데 데이터의 유형에 따라서 사실상 복호화가 가능할 수 있다. 예를 들어 특정한 사용자의 비밀번호가 ‘123456’이고 특정한 시스템에서 이 비밀번호를 MD5로 해시하여 데이터베이스에 저장했다고 가정해보자. 이 때 해커는 비밀번호 ‘000000’부터 ‘999999’까지를 반복적으로 MD5 해시를 적용하여 일종의 해시 사전(Hash Dictionary)을 구성할 수 있다. 이후에 특정한 해시 값을 이 사전에 대입하여 원래의 비밀번호를 찾아내는 프로그램을 작성하게 되면 사용자의 비밀번호인 ‘123456’이 해커에게 드러나게 되는 것이다. 이렇게 ‘특정한 해시 알고리즘을 적용해 만들어낼 수 있는 데이터를 모두 저장하여 만든 해시 사전’을 레인보우 테이블(Rainbow Table)이라고 부른다. 특정 서비스에 회원가입을 할 때 어려운 비밀번호를 설정해야 하는 이유는 바로 레인보우 테이블 공격 때문이다.


물론 레인보우 테이블을 막기 위한 다양한 방어 기법도 연구되었다. 그 대표적인 것은 바로 소금(Salt) 값을 넣는 방법이다. 개발자가 사전에 미리 정해 놓은 문자열을 입력 데이터에 추가하여 해시 결과를 저장하는 방식이다. 이처럼 ‘원본 데이터를 찾아내기 어렵도록 하기 위해 추가하는 문자열’을 소금이라고 한다. 예를 들어 소금 값이 ‘Teddy Bear’라고 가정했을 때 MD5에 문자열 ‘A’, ‘B’, ‘C’를 넣었을 때의 결과는 다음과 같다. 이와 같이 소금 값을 적절하게 대입하는 경우 악의적인 공격자는 해시 결과 값을 얻었다고 하더라도 원래 문자열이 ‘A’, ‘B’, ‘C’와 같은 형태였다는 것을 파악하기가 기하급수적으로 어려워진다. 따라서 실제 서버 운영 시에 개발자들은 레인보우 테이블을 막기 위해 소금 값을 반드시 적용한다.



[그림 5] 소금 값을 적용한 MD5 이용 사례 예시


하지만 실제로 해커에게 서버가 해킹을 당하는 경우에는 솔트 값을 적용하는 소스코드 또한 해커의 손에 넘어가는 경우가 많으므로 궁극적으로 가장 중요한 것은 사용자 본인이 암호를 얼마나 어렵게 설정했느냐다. 기본적으로 비밀번호에 특수문자, 알파벳, 숫자가 포함될 수 있도록 설계하면 하나의 문자 당 약 80개의 경우의 수를 가지게 된다. 이 때 사용자의 비밀번호가 12자리라면 80의 12제곱인 68,719,476,736,000,000,000,000 개의 경우의 수를 가지게 되므로 사실상 해커가 하나의 비밀번호 해시 데이터를 탈취하고, 소금 값까지 가지고 있다고 하더라도 원래의 비밀번호를 알아내기는 쉽지 않다. 하지만 특정한 사용자가 이용하는 모든 웹 사이트의 비밀번호가 동일하다면 해커는 그 비밀번호 하나만 복호화에 성공하면 모든 서비스를 해당 사용자의 권한으로 이용할 수 있을 것이다. 따라서 암호를 복잡하게 설정하는 것은 매우 중요하다.


MD5 복호화 실습하기


이제 한 번 MD5 복호화를 실습해보도록 하자. 먼저 저자는 독자들에게 하나의 암호문을 제시할 것이다. 앞서 저자는 레인보우 테이블(Rainbow Table)이라는 개념을 설명하여 독자들에게 힌트를 주었다. 정보보안에 재능이 있는 독자라면 저자가 제시하는 암호문이 의미하는 원래 문장을 찾아내는 방법을 금방 생각해낼 수 있을 것이다.



[그림 6] 특정한 의미를 가지고 있는 MD5 암호문 예제


위와 같은 암호화 문장을 해커의 입장에서 발견했다고 가정해보자. 이 때 이를 어떻게 복호화 할 수 있을까? 해시 암호문을 복호화하는 가장 대표적인 두 가지 방법은 ① 자신이 직접 자신의 컴퓨터에 사전 데이터베이스(Dictionary Database)를 생성한 뒤에, 이를 레인보우 테이블로 삼아 프로그램으로 많은 입력 값을 넣어 해시 결과 값 데이터베이스를 구축하는 방법, 그리고 ② 레인보우 테이블을 제공해주는 무료 사이트를 이용하는 것이다. 두 번째 방법이 자신의 컴퓨터 용량을 소비하지 않아도 된다는 점에서 보다 효율적이다. MD5 복호화를 위한 레인보우 테이블을 무료로 제공해주는 사이트의 URL(http://md5decryption.com/)로 들어가 보자.


위 사이트에서 저자가 제시한 암호문 ‘0CFDE61FEF91A54F5289BE8106FDB85D’을 넣고 실행해보면 결과 값인 ‘나동빈’이 나오는 것을 알 수 있다. 하지만 여기에서 의문점이 들 수 있다. 아무리 이 외국 웹 서비스가 방대한 양의 레인보우 테이블을 가지고 있을 지라도 어떻게 ‘나동빈’이라는 저자의 한국 이름을 가지고 있는 것일까 하는 의문점 말이다. 사실 이는 이 웹 서비스가 현재 방대한 양의 레인보우 테이블을 가지게 된 이유를 고려해보면 이해하기 쉽다. 해당 웹 서비스는 MD5 복호화 기능 말고도 암호화 서비스를 제공하고 있다. 이 때 사용자가 암호화를 위해 입력한 문장을 레인보우 테이블에 새롭게 삽입하는 것이다. 즉 저자는 ‘나동빈’이라는 문장을 먼저 해당 사이트에서 암호화를 시켜놓았기 때문에 이 사이트에서 복호화가 가능한 것이다.


MD5 해킹 실습하기


해시의 개요 챕터의 마지막으로 다룰 내용은 바로 MD5 해킹 실습이다. 구체적으로 ‘서로 다른 역할을 수행하는 두 파일의 MD5 해시 값이 같도록, 즉 충돌이 발생한 것처럼 타인을 속이는 실습’을 해보자. 이를 위해 특정한 파일의 바이너리 코드를 MD5 해시 결과로 바꾸어주는 프로그램을 이용하면 된다. 국내 개발자가 개발한 파일 해시 계산기(File Hash Calculator)를 공식 URL(http://www.owlnetworks.pe.kr/software/calcfilehash)에서 다운로드 받아 실행해보자.



[그림 7] 파일 해시 계산기 실행 결과


이제 ‘찾아보기’ 버튼을 눌러서 아무 파일이나 파일 해시 계산기에 넣은 뒤에 해시를 계산해보자. 저자는 한 번 현재 작성하고 있는 원고 파일(HWP)을 넣어보았다. 그 결과 MD5 해시 값으로 ‘79D4BE7B9C390E30ECC7B5BA8ACEB337’가 출력되었다. 현재 작성하고 있는 원고는 이러한 해시 결과 값으로 상징된다는 것이다. 아마 독자가 특정한 파일을 넣을 때마다 MD5 해시 결과 값은 항상 다를 것이다. 만약에 넣은 두 개의 파일의 해시 결과 값이 우연히 동일하게 나온 독자가 있다면 바로 로또를 구매하라고 추천하고 싶다. 단순히 계산했을 때 확률적으로 로또 1등에 760,620,663,133,661,799,251,010,032 번 당첨된 셈이니까.



[그림 8] 파일 해시 계산기 실습 결과


이제 한 번 완전히 다른 역할을 수행하는 두 개의 파일의 MD5 해시 값이 동일하도록 만들어 보자. 사실 지금 소개할 방법은 MD5 뿐만 아니라 어떠한 해시 알고리즘에 적용하더라도 동일하게 작동하는 방법이다. 일단 저자는 다음과 같이 두 개의 파일을 준비해보았다.



[그림 9] 해시 충돌 눈속임 준비물


이후에 명령 프롬프트(CMD)를 실행해서 두 파일이 위치한 폴더로 이동한다. 이 때 폴더 이동명령어(Change Directory, CD)를 이용한다. 그리고 이어서 copy /b 명령어를 이용해 두 개의 파일을 합친 파일을 만들어보자. 저자는 차례대로 result.jpg와 result.zip이라는 이름으로 결과 파일을 생성해주었다.



[그림 10] 서로 다른 두 파일을 명령 프롬프트를 이용해 합치는 방법


이제 두 결과 파일은 다음과 같다. 둘 다 파일의 용량이 늘어난 것을 제외하고는 기존의 image.jpg 파일과 hided.zip 파일과 동일하게 작동한다. 하지만 실제로 바이너리 코드의 관점에서는 두 파일은 완전히 동일한 파일이라고 할 수 있다.



[그림 11] 해시 충돌 눈속임 결과 파일


한 번 파일 해시 계산기를 이용해 두 결과 파일의 해시 값을 확인해보자. 저자의 경우 생성한 두 파일의 MD5 해시 값이 모두 ‘D7BF42DD206E03A72526F5609E1CBB4C’라는 값을 가지고 있는 것을 확인할 수 있었다. 이와 같이 간단하게 서로 다른 역할을 수행하는 파일이 완전히 동일한 바이너리 코드를 가지게 할 수 있는 것이다.


[그림 12] 해시 충돌 눈속임 결과 파일의 해시 결과 값


이처럼 해시는 무결성이라는 정보보안 요소의 핵심 알고리즘이며 다양한 편법과 기술이 존재한다는 점에서 알고 있으면 도움이 되는 개념이다.


http://www.bitweb.co.kr/news/view.php?idx=360

비트코인과 블록 체인

블록 체인은 비트코인의 바탕이 되는 ‘체계’이며,

비트코인은 블록 체인을 ‘화폐’에 응용한 결과물이다.

블록 체인이 바탕이고, 비트코인은 블록 체인 바탕위에서 구현된 하나의 서비스 또는 상품이라고 할 수 있다. 그래서 블록 체인은 비트코인 뿐아니라 다른 코인의 바탕이 될 수도 있고 실제로도 그렇다. 또한 블록 체인은 코인 뿐아니라 다른 서비스나 상품의 바탕이 될 수도 있다.

재미있는 것은 블록 체인이 비트코인의 바탕이 되는 체계이지만, 블록 체인이 만들어지고 비트코인이 만들어진 것이 아니라 비트코인을 만들기 위해 고민하던 중에 블록 체인이라는 기술이 탄생했다는 점이다.

이제부터 블록 체인이 어떻게 비트코인이라는 화폐의 바탕이 되는 체계가 될 수 있는지 알아보자. 먼저 화폐가 무엇인지 짧게 되새겨 보자.

화폐는 믿음이다

현대 사회에서 화폐란 무엇일까? 너무 모호한 것 같으니 질문을 바꿔보자. 현대 사회에서 화폐의 ‘실체’란 무엇일까?

과거에는 금이나 은 등 실제 실물이 화폐의 역할을 담당했지만, 점차 그 실물에 대한 교환 증서가 화폐의 역할을 담당하게 되었고, 1971년의 닉슨 쇼크 이후에는 사실상 그 실물(금)에 대한 교환도 보장해 주지 않는 불태환 화폐가 되었고, 2008년 금융 위기 이후에는 양적 완화라는 미명 하에 막대한 양의 화폐를 찍어내기도 했다. 그럼에도 불구하고 여전히 우리는 그 화폐를 가지고 큰 문제 없이 살아가고 있다. 어째서 이런 일이 가능할까?

화폐는 ‘믿음’이다.

그렇다. 사실 우리는 화폐를 믿고 있다. 화폐의 실체는 바로 믿음이다. 금으로 교환해준다는 보장은 사라졌지만, 화폐로 여전히 치킨을 먹고 맥주를 마실 수 있다고 모두가 믿고 있다. 화폐는 모두의 ‘신뢰’를 받고 있는 것이다. 비트코인이 가상 화폐, 디지털 화폐 또는 암호화 화폐처럼 화폐라는 용어를 달고 있는 이유도 ‘신뢰’할 만한 대상이 될 수 있기 때문이다. 그 신뢰의 원천이 바로 블록 체인이다.

그럼 블록 체인이 어떻게 신뢰의 원천이 될 수 있는지 차례차례 알아보자.

분산 공개 장부

A가 B에게 만원을 보내는 상황을 생각해보자. A가 은행 사이트에 접속해서 로그인하고, 잔액을 확인하고, B의 계좌 정보를 입력한 후에, 공인인증서나 OTP 등의 비밀번호를 입력하고 확인 버튼을 누르면, A의 잔액에서 만원이 차감되고, B의 잔액에 만원이 더해진다.

개인의 재산이 오가는 이 송금 과정은 매우 중요하다. 그래서 송금 거래에 필요한 모든 확인 과정은 은행에서 수행되며, 그 기록도 은행에 저장되고 외부로는 공개되지 않는다.

그래서 송금에 대해 우리가 믿는 곳은 은행 하나다. 송금 과정에 있어서의 모든 것이 은행 하나에 집중되어 있다. 이는 바꿔 말하면 은행이 바로 단일 실패 지점(Single Point of Failure)이라는 말이다. 즉, 거래 은행의 서버가 정상적으로 동작하지 않거나, 은행의 기록이 사라지게 되면 우리는 재산을 잃게 된다.

컴퓨팅 분야에서 이런 단일 실패 지점 문제를 해결하는 보편적인 방법은 고가용성 처리, 쉽게 말해 다중화다. 2중, 3중으로 복제나 분산처리를 해서 단일 실패 지점을 없애는 전략을 취한다. 은행 시스템도 이런 다중화 처리가 되어 있으므로 앞에서 말한 어처구니 없는 불상사는 쉽게 일어나지 않는다. 소프트웨어 뿐 아니라 보안 담당 직원 배치, 보안 담당 장비 도입 등으로 더 한층 위험을 낮춘다. 하지만 이런 조치를 위해서는 많은 비용이 필요하다.

블록 체인은 이 문제를 완전히 다른 각도에서 바라본다(각주: 설명의 단순화를 위해 이후로 퍼블릭 블록 체인만을 대상으로 한다). 즉, 거래 정보를 감추지 않고 모두에게 공개하고, 누구나 거래 정보를 생성할 수 있으며, 거래 정보를 모두에게 복사해서 사본을 저장하고 그 사본끼리 동기화시킨다. 쉽게 말해 2중화, 3중화 정도가 아니라 수천중화, 수만중화 처리를 해서 기록이 사라지는 일을 원천적으로 막아버린다. 그래서 블록 체인을 거대한 분산 공개 장부라고 부르기도 한다.

블록 체인은 거대한 분산 공개 장부다.

이렇게 수천 수만의 분산 처리를 통해 기록의 멸실은 막을 수 있지만, 이것만으로는 멸실만큼이나 위험한 기록의 위/변조까지 막을 수는 없다. 블록 체인은 이 문제를 암호학의 도움을 받아서 해결한다.

디지털 서명

개발자라면 디지털 서명에 대해 희미하게라도 알고 있겠지만, 블록 체인을 얘기할 때 빼놓을 수 없는 중요한 부분이므로 다시 한 번 짚고 넘어가자.

디지털 서명은 비대칭키와 해쉬 함수를 이용해서 다음과 같은 방식으로 데이터의 진위 여부를 확인할 수 있게 해준다.

Imgur

블록 체인에 담겨 있는 모든 거래 정보에는 이렇게 디지털 서명이 포함되어 있어서 거래 정보가 진본임을 신뢰할 수 있다.

블록이란?

자 이제부터 진짜 블록 체인의 내부로 들어가 볼 차례다. 블록 체인이라는 체인을 이루는 원소인 블록은 무엇일까?

블록은 유효한 거래 정보의 묶음이다.

“A가 B에게 100원을 송금한다.”와 같은 것이 하나의 거래이며, 하나의 블록에는 여러 개의 거래가 포함된다. 블록에 대해 더 구체적으로 이해하려면 실제 구현체를 살펴보는 것이 효율적이다. 그래서 블록 체인의 최초 구현체인 비트코인을 기준으로 블록과 블록 체인에 대해 살펴볼 것이다. 구현체마다 세부적인 방식은 다를 수도 있지만 큰 줄기는 거의 같다.

비트코인의 블록 하나에는 평균 약 1,800개의 거래 정보가 포함될 수 있으며, 블록 하나의 물리적인 크기는 평균 0.98Mbyte이다(출처: https://blockchain.info/charts). 블록은 블록 헤더와 거래 정보, 기타 정보로 구성된다.

  • 블록 헤더는 versionpreviousblockhashmerklehashtimebitsnonce 이렇게 6개의 정보로 구성된다.
  • 거래 정보는 입출금과 관련한 여러가지 정보를 가지고 있다.
  • 기타 정보는 블록 내에 있는 정보 중에서 블록 헤더와 거래 정보에 해당하지 않는 정보를 말하며, 블록 해쉬 계산에 사용되지 않는다.

이 중에서 블록 체인의 메커니즘을 이해하는 데 중요한 열쇠가 되는 정보는 블록 헤더다.

블록 헤더

블록 헤더는 다음의 6가지 정보로 구성된다.

  1. version : 소프트웨어/프로토콜 버전
  2. previousblockhash : 블록 체인에서 바로 앞에 위치하는 블록의 블록 해쉬
  3. merklehash : 개별 거래 정보의 거래 해쉬를 2진 트리 형태로 구성할 때, 트리 루트에 위치하는 해쉬값
  4. time : 블록이 생성된 시간
  5. bits : 난이도 조절용 수치
  6. nonce : 최초 0에서 시작하여 조건을 만족하는 해쉬값을 찾아낼때까지의 1씩 증가하는 계산 회수

블록 헤더가 중요한 이유는 블록의 식별자 역할을 하는 블록 해쉬가 이 블록 헤더의 6가지 정보를 입력값으로 해서 구해지기 때문이다.

블록 해쉬

블록의 식별자 역할을 하는 블록 해쉬는 6가지의 블록 헤더 정보를 입력값으로 하고, 여기에 SHA256 해쉬 함수를 적용해서 계산되는 값으로, 32바이트의 숫자값이다. 이름은 블록 해쉬이지만 그 값은 블록 전체를 해쉬한 값이 아니라, 블록 헤더를 해쉬한 값이다.

지금까지의 내용을 바탕으로 블록 해쉬를 구하는 과정을 그림으로 나타내면 다음과 같다.

Imgur

개별 거래 정보는 결국 머클 트리의 해쉬값인 merklehash 값으로 집약된다. 블록 헤더의 6가지 정보 중에서 versionpreviousblockhashmerklehashtimebits 이렇게 5가지는 블록 해쉬를 만드는 시점에서 이미 확정되어 변하지 않는 값이다. 하지만 마지막 nonce는 확정되어 있지 않고 새로 구해야 하는 값이다. 이 nonce 값을 구해서 최종적으로 블록 해쉬 값을 구하고, 이 블록 해쉬값을 식별자로 가지는 유효한 블록을 만들어내는 것이 바로 작업 증명(Proof of Work), 흔히 말하는 채굴이다.

작업 증명에 대해 알아보기 전에, 이제 블록이 무엇인지 기술적인 수준에서 알게 되었으니 블록이 이어져서 만들어지는 블록 체인도 기술적인 관점에서 정리해보자.

블록 체인이란?

블록 체인은 앞에서는 거대한 분산 공개 장부라고 했는데, 기술적으로는 블록이 이어져서 만들어진 블록의 집합체라고 말할 수 있다.

블록들은 어떻게 이어져 있는걸까? 앞에서 살펴본 블록 헤더의 6가지 정보 중의 하나인 previousblockhash 값은 현재 생성하고 있는 블록 바로 이전에 만들어진 블록의 블록 해쉬값이다. 블록은 바로 앞의 블록 해쉬 값을 포함하는 방식으로 앞의 블록과 이어지게 된다.

Imgur

어디서 많이 본 자료 구조 아닌가? 그렇다. 블록 체인은 사실 링크드 리스트다.

블록 체인은 블록으로 이루어진 링크드 리스트다.

블록 체인을 기술적인 측면에서도 살펴봤으니 이제 그 내부로 조금 더 깊게 들어가보자.

블록 체인의 첫번째 핵심 - 작업 증명

자료 구조 관점에서는 링크드 리스트일 뿐인 블록 체인이 신뢰의 원천이 될 수 있는 첫번째 핵심 요소는 작업 증명(Proof of Work)이다.

앞에서 nonce값을 구해서 최종적으로 블록 해쉬값을 구하고, 이 블록 해쉬값을 식별자로 가지는 유효한 블록을 만들어내는 것이 작업 증명이라고 했다. 결국 nonce 값을 구하는 것이 작업 증명의 핵심이다. nonce값을 구하는 방법을 알아보기 전에 먼저 nonce값이란 어떤 값을 의미하는 지 알아야 한다.

nonce값은, 이 nonce값을 입력값 중의 하나로 해서 계산되는 블록 해쉬값이 특정 숫자보다 작아지게 하는 값을 말한다.

그럼 이 nonce값은 어떻게 구할 수 있을까? 위의 설명에서 맨 마지막 부분을 떼어보면 해쉬값이 특정 숫자보다 작아지게 하는 값이라고 한다. 해쉬 함수의 특성상, 어떤 해쉬값(A라고 하면)을 결과로 나오게 하는 입력값을 찾으려면, A에서 역산을 하는 방식으로는 찾을 수 없고, 결과가 A가 될 때까지 무작위로 입력값을 계속 바꿔가면서 해쉬값을 계산해보면서 찾아낼 수 밖에 없다.

그렇다면 어떤 블록 해쉬값이 어떤 특정 숫자보다 작아지게 하려면, 블록 해쉬의 입력값을 계속 바꿔가면서 구한 해쉬값이 특정 숫자보다 낮은지 비교하는 작업을 계속 반복하는 수 밖에 없다.

블록 해쉬의 입력값은 블록 헤더고, 블록 헤더에는 6가지 정보가 담겨 있으며, 이 중에서 5가지는 값이 고정되어 변경할 수 없고, 오직 nonce만 값을 바꿀 수 있다.

즉, 이 nonce값을 계속 바꿔가면서 계산한 해쉬값이 어떤 특정 숫자보다 작다면, 그 때의 nonce값이 새로 만들어지는 블록의 nonce값으로 확정되고, 특정 숫자 보다 작게 나온 그 해쉬값이 새로 생성되는 블록의 블록 해쉬값으로 최종 확정되며 작업 증명이 끝나게 된다.

이 과정을 그림과 함께 살펴보자. 아래 그림은 블록해쉬가 000000a84...라는 특정값보다 작게 나오게 하는 nonce값을 구하는 과정을 나타내고 있다.

Imgur

앞에서 설명한 대로 nonce 외의 모든 항목의 값은 이미 정해져 있다.

nonce 값이 0 일 때의 해쉬를 구해보니 000000a84...보다 큰 fa8cbaeed... 가 나와서 작업 증명에 실패 했다.

다시 nonce가 1 일 때의 해쉬를 구해보니 932d16e2e... 가 나와서 또 실패.. nonce가 2 일 때의 해쉬, 3 일 때의 해쉬.. 를 계속 반복해서 구하고 실패하다가, nonce가 82,764,351 일 때의 해쉬를 구해보니 드디어 000000a84... 보다 작은 000000a83...이 나와서 작업 증명에 성공했다.

그렇다면 작업 증명 성공 여부의 기준이 되는 000000a84...라는 값은 어떻게 정해지나? 그 값은 작업 난이도에 의해 결정되는데 이 부분은 핵심은 아니므로 그보다 더 중요한 보상에 대해 알아보자.

보상

이렇게 nonce 값을 구하는 채굴 작업에는 엄청나게 많은 횟수의 해쉬 계산이 필요하며 그런 계산을 수행하는 채굴기와 그 채굴기를 돌릴 수 있는 막대한 양의 전기라는 비용이 든다. 그래서 이런 비용에 대한 보상이 없다면 아무도 채굴을 하지 않을 것이다.

비트코인에서의 보상은 새로 발행되는 비트코인과 해당 블록에 포함되는 거래의 거래 수수료의 합이다. 비트코인의 새로운 발행은, 채굴자가 블록을 처음 구성할 때 채굴자의 지갑으로 일정량의 비트코인이 입금되는 거래를 그 블록의 첫 거래(generation transaction)로 추가하는 방식으로 이루어진다.

Imgur

새로 발행되는 비트코인은 최초에 50BTC에서 시작해서 블록 체인에 21만개의 블록이 추가될 때마다 절반으로 줄어들어 2017년 7월 현재는 블록 당 12.5BTC가 새로 발행되며, 보상의 일부로서 채굴자에게 주어진다.

거래 수수료는 각 거래 당사자끼리 자율적으로 정할 수 있고, 거래가 블록에 추가되는 우선 순위를 결정하는데 거래 수수료가 입력값으로 사용되기도 한다.

보상은 nonce 값을 찾아내고, 그 결과 새로운 블록을 블록 체인에 추가해서,

해당 블록에 포함된 모든 거래를 유효한 거래로 확정시켜준 대가라고 할 수 있다.

블록 체인의 두번째 핵심 - 충돌 해소

블록 체인의 첫번째 핵심은 작업 증명이며, 이는 블록 단위에서 처리되는 일이다. 블록 체인의 두번째 핵심은 충돌 해소인데, 이는 블록 단위가 아니라 블록 체인 단위에서 처리되는 일이다. 어떤 충돌을 의미하는 것인지 또 왜 해소시켜야 하는지 알려면 블록 체인을 분산 환경에서 바라볼 필요가 있다.

거래 정보의 전파

앞에서 블록 체인은 거대한 분산 공개 장부라고 했다. 이 분산 공개 장부는 여러 개의 노드에 복사되어 있으며, 여러 개의 노드는 p2p로 연결되어 블록 체인 네트워크를 형성한다. 그리고 하나의 거래 정보가 발생하면 이 거래 정보는 블록 체인 네트워크에 분산되어 있는 수많은 노드에 전파되어야 한다.

다음 그림과 같이 사용자 F가 지갑 앱에서 C에게 수수료 0.001BTC와 함께 1.6BTC를 보내면, 지갑 앱은 블록 체인 네트워크 상의 노드 A에 거래 정보를 전송한다.

거래 정보를 받은 노드 A는 먼저 해당 거래의 유효성을 검증한 후에 그 거래를 아직 블록 생성 작업이 시작되지 않은 후보 블록에 추가하고, 인접한 다른 노드에 그 거래 정보를 전파한다.

거래 정보를 전파받은 노드 B도 마찬가지 방식으로 블록 체인 네트워크 상의 다른 노드에게 거래 정보를 전파하며, 이 거래는 결국 블록 체인 네트워크 전체 노드에 전파된다.

Imgur

블록의 생성 및 전파

블록에 거래 정보가 채워지면 노드는 블록을 생성한다. 이때 캐나다에 있던 노드와 호주에 있던 노드는 상당히 멀리 떨어져 있으므로, 각 블록에 담겨 있는 거래의 내용과 순서는 아래와 같이 서로 다를 수 있다.

Imgur

새로 만들어질 블록은 다음 그림(출처: Mastering Bitcoin - http://chimera.labs.oreilly.com/books/1234000001802/ch08.html#forks) 과 같이 현재 마지막 블록인 파랑블록(P블록)의 다음에 추가될 예정이다.

Imgur

이 때 캐나다의 노드와 호주의 노드가 거의 동시에 nonce 값을 찾아서 블록을 성공적으로 생성했다. 캐나다의 노드가 생성한 블록을 빨강블록(A블록)이라 하고, 호주의 노드가 생성한 블록을 초록블록(B블록)이라하면, 생성 후 전파 과정은 다음 그림(출처: Mastering Bitcoin - http://chimera.labs.oreilly.com/books/1234000001802/ch08.html#forks) 과 같다.

Imgur

캐나다 노드의 인접 노드에서는 전달받은 빨강블록의 블록 해쉬를 다시 계산해서 그 값이 정말로 특정한 숫자보다 작은 올바른 값인지 검증한 후에, 자신이 가지고 있던 파랑블록에 빨강블록을 추가한다.

호주 노드에 인접한 노드에서도 마찬가지 방식으로 기존의 파랑노드에 초록노드가 추가된다. 이 방식이 계속되면서 빨강블록과 초록블록은 전 세계에 분산되어 있는 노드에 전파된다.

블록 체인의 분기

포르투갈에 있는 노드에는 빨강블록이 먼저 전파된 후에 초록블록이 전파파되었다. 포르투갈 노드에서는 늦게 도착한 초록블록은 무시된다.

러시아에 있는 노드도 나름 열심히 nonce 값을 구하고 있었으나, 구하기 전에 아쉽게도 초록블록을 먼저 전달받아서 초록블록을 검증한 후 파랑블록 다음에 초록블록을 추가했다. 초록블록에 이을 새로운 블록의 nonce값을 구하기 시작하고, 잠시 후 빨강블록을 전달받았다. 늦게 도착한 빨강블록은 러시아 노드에서는 무시된다.

이번에는 러시아 노드가 가장 먼저 nonce 값을 구해서 새로 분홍블록(X블록)을 생성하고 초록블록 다음에 분홍블록을 추가한 후에 다음 그림(출처: Mastering Bitcoin - http://chimera.labs.oreilly.com/books/1234000001802/ch08.html#forks) 과 같이 인접 노드에 전파했다.

Imgur

포르투갈에 있던 노드에는 파랑블록 다음에 빨강블록이 추가되어 있는 상태였는데, 파랑블록, 초록블록에 이어진 새로운 분홍블록을 전달받았다. 포르투갈에 있던 노드에는 다음과 같이 블록 체인의 분기가 발생한다.

Imgur

빨강블록과 초록블록의 내용은 서로 다르다. 블록 체인은 신뢰의 원천이 되는 체계라고 했는데, 이렇게 정보가 갈라지는 체계를 믿을 수 있는걸까?

어느 블록 체인을 믿을 것인가?

블록 체인에서는 이렇게 분기가 발생해서 충돌이 일어날 때 어느 블록 체인을 믿을 것인가에 대한 기준이 마련되어 있다. 그렇지 않으면 분산되어 있는 장부 내용의 동기화가 깨지기 때문이다. 그 기준은 다음과 같다.

블록 체인에 분기가 발생하여 충돌하게 될 때는 더 많은 작업 증명이 수행되어 길이가 더 긴 블록을 선택한다.

그래서 포르투갈 노드에 더 긴 블록 체인을 가진 분홍블록이 전파되는 순간, 빨강블록은 파랑블록에서의 연결이 끊어지고 고아가 된다.

Imgur

블록 생성은 평균 10분이 소요될 정도로 연산량이 큰 작업이며, 따라서 앞에서 발생한 것처럼 A블록과 B블록이 거의 동시에 생성되어 분기가 발생할 가능성은 그리 높지 않다. 그리고 길이가 같은 블록 체인이 충돌하더라도 머지 않아 블록 체인의 길이가 달라져서 분기에 의한 충돌이 해소되는 순간이 금방 다가온다.

아래의 그림은 길이가 긴 블록 체인에 의해 고아가 되는 블록의 수를 나타내는데, 최근 1년간 대부분 2개 이하, 최대 3개 이내에서 블록 체인의 분기에 의한 충돌이 해소됨을 알 수 있다.

Imgur

하지만 일시적으로나마 이런 분기 상태가 발생할 수 있기 때문에, 실제 거래 상황에서는 보통 어떤 거래가 포함된 블록 이후로 3-5개의 블록이 더 추가된 후에야 그 거래를 최종적으로 유효한 것으로 확정한다. 일시적으로 분기가 발생하더라도 그 이후로 3-5개의 블록이 추가되는 과정에서 분기 상태가 해소되고 결국 하나의 블록 체인만 남기 때문이다.

고아가 된 A블록에 있던 거래 중 유실되는 거래가 생기지 않을까?

빨강블록에 있던 거래 중에서 초록블록에 포함되지 않았던 거래T가 있을 수 있다. 그럼 빨강블록이 고아가 되면 거래T는 결국 유실되는 것이 아닐까하는 의문이 들 수 있다.

하지만 유실은 발생하지 않는다거래T는 초록블록에는 포함되지 않았더라도 분홍블록 또는 분홍블록 이후의 블록에 결국에는 포함되게 된다. 왜냐하면, 분홍블록은 초록블록을 부모로 해서 생성되는데 거래T가 초록블록에 포함되지 않았으므로, 초록블록에 이어 생성되는 분홍블록 또는 분홍블록 이후의 블록에서는 거래T를 아직 블록 체인에 포함되지 않은 다른 거래들과 마찬가지로 취급하며 블록에 추가하기 떄문이다.

이중 지불 문제

디지털은 복사가 가능하므로 이중 지불이라는 문제가 항상 따라다닌다. 블록 체인도 예외는 아니다. 예외는 아닌 정도가 아니라 수많은 노드에 복제되는 방식이므로 이중 지불 문제가 반드시 생길 것 같다.

일단 동일한 기기에 담긴 지갑에서는 이중 지불을 할 수 없다. 예를 들어 하나의 기기에 설치되어 있는 지갑의 잔액이 5만원일 때, 이 지갑에서 5만원을 지불하는 순간 잔액이 0이 되고, 다시 5만원을 지불할 수 없기 때문이다.

그래서 이중 지불은 보통 물리적으로 떨어진 두 개의 지점에서 생각해 볼 수 있는 문제다. 장부가 복제되어 있으므로 캐나다에서도 내 잔액은 5만원이고, 호주에서도 내 잔액은 5만원이다. 그럼 캐나다에서도 A에게 5만원을 보내고, 이 거래가 아직 도달하지 않은 호주에서도 B에게 5만원을 보내면 어떻게 될까?

시작할 때는 멀리 떨어진 두 곳에서 이중 지불이 실행 되었지만, 두 거래는 블록 체인 네트워크를 타고 전파되다가 어디에선가 반드시 만나게 되어있다. 그 만나는 지점에서는 두 거래 사이에 반드시 선후 관계가 생긴다. 그 지점에 먼저 도달한 거래는 유효한 거래로 인정되지만 늦게 도달한 거래는 이미 잔액이 0인 상태에서 5만원을 보내게 되므로 무효한 거래로 버려지게 된다.

따라서 이중 지불된 거래 중 하나는 결국에는 무효화되는 방식으로 이중 지불 문제가 해결된다.

블록 체인의 세번째 핵심 - 완료된 거래 정보의 변경 불가

거래 정보의 해쉬값은 해당 거래가 포함된 블록의 merklehash 계산에 입력값으로 사용되고, merklehash는 블록 해쉬의 계산에 입력값으로 사용된다. 블록 해쉬는 다음 블록(M이라 하면)의 previousblockhash 값으로 저장되며, 이 previousblockhash은 M블록의 블록 헤더 정보로서, M블록의 블록 해쉬를 계산하는데 입력값으로 사용된다.

Imgur

따라서, 어떤 거래 정보가 변경되면 그 거래 정보가 포함된 머클트리의 merklehash가 변경되고, merklehash가 변경되면 블록 해쉬가 변경된다.

그러면 아래 그림에서 빨간색 밑줄로 표시한 변경된 블록 #1의 블록 해쉬와 그 다음 블록인 블록 #2의 블록 헤더에 previousblockhash로 저장된 값이 달라지게 된다. 따라서 체인을 유지하려면 블록 #2의 previousblockhash 값을 갱신한 후에 블록 #2의 nonce 값을 다시 구해서 블록 해쉬를 새로 구해야 하고, 연이어 블록 #3, #4의 블록 해쉬도 모두 새로 계산해야 한다.

Imgur

그리고 블록 해쉬는 작업 증명의 해답(nonce 값)을 찾아내야 구할 수 있으므로, 거래 정보를 변경한 블록부터 그 이후의 모든 블록을 순서대로 다시 채굴해야 한다.

블록 하나 채굴하는데 평균 10분이 소요되므로 어떤 악의적인 노드가 바로 앞의 블록의 거래 정보를 변경하고 채굴하는 그 10분 동안, 다른 선의의 노드들은 거래 정보가 변경되지 않은 원래의 블록체인에 계속 블록을 이어 나가게 된다. 그래서 그 10분 후에는 악의적인 노드의 블록 체인의 길이는 다른 선의의 노드들이 보유한 블록 체인의 길이보다 1개 더 짧아지게 되고, 두 블록 체인이 만나게 되는 순간 길이가 짧은 블록 체인은 버려지게 된다.

완료된 거래 정보를 변경하려면,

변경하려는 거래 정보가 포함된 블록부터 그 이후의 모든 블록을 순서대로 다시 채굴해서 새로운 블록 체인(A)를 만들어야 하는데,

그동안에도 변경 되지 않은 원래의 블록 체인에는 다른 노드들에 의해 블록이 계속 추가되고 A보다 길이가 길어지게 되므로,

A는 폐기되며 완료된 거래 정보의 변경은 실패하게 된다.

그런데 악의적인 노드가 다른 노드들보다 연산 능력이 훨씬 뛰어나다고 가정해보자.

그렇다면 악의적인 노드에 있는 블록 체인에 블록이 추가되는 속도가 다른 블록 체인에 블록이 추가되는 속도보다 더 빠를 것이고, 언젠가는 악의적인 노드의 블록 체인의 길이가 가장 길어지게 된다. 이렇게 되는 순간 악의적인 노드에 의해 변경된 거래 정보가 유효한 거래 정보로서 전체 블록 체인 네트워크에 퍼지게 되며 과거 거래 정보의 변경이 성공하게 된다. 이를 51% 공격이라고 한다.

하지만 경제적인 관점에서 생각해보면 이런 일이 발생할 가능성은 사실상 없다. 일단, 거래 정보가 변경될 수 있다는 사실이 알려지는 순간 블록 체인의 신뢰는 깨지게 된다.

만약 악의적인 노드가 오랫동안 가장 큰 연산 능력을 가지고 있었다면, 악의적인 노드가 생성한 블록이 많을 것이고 그에 따른 보상액도 많이 보유하고 있을 것이다. 이런 상황에서 블록 체인의 신뢰가 붕괴되면 큰 피해를 보는 쪽은 악의적인 노드 자신이기 떄문에 거래 정보를 변경할 경제적 동기가 없다.

악의적인 노드가 갑자기 많은 연산 능력을 가지게 되었다고 해도, 블록 체인의 신뢰 붕괴로 가치가 사라진 블록을 채굴해봤자 앞으로 가져갈 수 있는 경제적 이익은 없다. 따라서, 경제적으로 이익을 볼 수 없는 거래 정보의 변경은 사실상 발생하지 않게 된다.

마무리

지금까지 블록 체인이 어떻게 비트 코인이라는 암호화 화폐의 신뢰의 원천이 될 수 있는가라는 물음에 대한 답을 찾아봤다. 정리해보면 다음과 같다.

블록 체인은

  • 거대한 분산 공개 장부이며, 그 장부 안에 포함된 개별 거래는 모두 디지털 서명이 붙어 있어서 은행이나 다른 제3자의 개입이 없어도 진본임을 보증할 수 있다.
  • 수천, 수만노드에 분산 되어 있어서 어느 한 지점에 장애나 공격이 발생하더라도 블록 체인이라는 네트워크 전체는 문제 없이 계속 돌아갈 수 있다.
  • 작업 증명이라는 수학적 계산 작업과 경제 관점에서의 논리를 통해 위/변조가 사실상 불가능한 구조를 갖게 되어, 그 안에 기록된 거래들은 은행같은 중앙의 보증 기관이 없이도 신뢰할 수 있는 거래로서 확정될 수 있다.
  • 분산 환경에 전파되는 과정에서 분기가 발생할 수 있으나, 가장 길이가 긴 블록 체인을 유효한 블록 체인으로 선택한다.

블록 체인에도 여러 가지 도전적인 과제들이 있고, 그중에서 가장 중요한 것은 블록 체인의 확장성 문제다. 여기에서 그런 내용을 모두 다룰 수는 없겠지만, 지금까지 펼쳐놓은 이야기가 앞으로 블록 체인에 대한 자료를 접할 때 이해의 폭을 넓혀줄 수 있는 발판이 되어줄 수 있기를 바란다.

블록 체인은 거래 당사자간의 신뢰 확보를 위해 중앙 기관을 필요로 하지 않는 탈중앙화(Decentralization)를 달성한 최초의 소프트웨어 기술이다.

비트코인은 화폐에 한정되어 있지만 이더리움이나 최근 개발되고 있는 EOS나 IOTA, 국내에서 개발되는 BlockchainOS 등은 단순한 화폐를 넘어서 블록 체인 위에서 당사자간의 계약을 프로그램으로 실행시킬 수 있는 탈중앙화 플랫폼을 지향하고 있다.

블록 체인이 세상을 바꿀 수 있는 기술이라고 평가받는 이유도 이처럼 탈중앙화 플랫폼의 바탕이 되기 때문이다.

좀더 나은 세상을 만드는데 기여할 수 있는 기회의 문이 천천히 하지만 분명히 열리고 있다.

FAQ로 정리해보는 블록 체인

블록을 생성하는데 성공한 단 한 명의 채굴자만 보상을 받는건가?

그렇다. 그래서 이론적으로는 평생 채굴기를 돌려도 단 하나의 블록도 생성하지 못할 수도 있다. 그래서 실제로는 채굴 풀(pool)을 형성해서 nonce 값을 찾는 계산 작업을 분담하고, 해당 풀에서 블록이 생성되면 풀에 참가한 채굴자들끼리 각자의 배분 기준에 의해 보상액을 배분 받는 방식으로 채굴 시장이 운영된다.

작업 난이도는 무엇인가?

블록 해쉬가 특정 숫자보다 낮게 나올 때의 nonce 값을 찾아내는 것이 작업 증명이라고 했다. 작업 난이도는 nonce값 계산의 어려운 정도를 나타낸다. 작업 난이도는 블록 헤더 정보에서 bits라는 값으로 조절된다.

앞에서 블록 해쉬는 32바이트의 숫자라고 했는데, 이해를 쉽게 하기 위해 블록 해쉬를 부호 없는 1바이트의 숫자라고 해보자. 그럼 1바이트의 숫자값을 블록 해쉬값으로 산출하는 해쉬 함수는 0 ~ 255 사이의 값을 결과로 산출한다.

블록 해쉬가 128보다 작아야 한다고 하면, 0 ~ 255 사이의 값을 산출하는 해쉬 함수를 적용해서 128보다 작은 블록 해쉬값이 나올 확률은 128보다 작은 수(0~127)의 개수 = 128/해쉬 함수가 산출할 수 있는 모든 값(0~255)의 개수 = 256, 즉, 128/256이므로, 50%의 확률이다.

블록 해쉬가 64보다 작아야 한다면 64/256, 즉 25%의 확률로 nonce 값을 구할 수 있다. 블록 해쉬가 32보다 작아야 한다면 확률은 12.5%로 줄어든다. 여기서 128, 64, 32라는 특정 숫자가 바로 블록 헤더 정보의 bits이다.

실제로 bits의 값이 128, 256 이런 식으로 저장되지는 않고, 지수와 계수를 사용하는 별도의 표현 방식이 있다.

난이도는 2,160개의 블록이 생성되는데 소요되는 시간이 평균 시간인 21,600분(10분/블록 * 2,160블록)보다 오래 걸리면 낮아지고, 적게 걸리면 높아지는 방식으로, 대략 21,600분을 주기로 전체적으로 평균 10분이 소요되는 하나의 난이도가 전체에 적용된다. 따라서 채굴자가 늘어나서 블록을 생성하는데 소요되는 시간이 줄어들게 되면, 정해진 주기에 따라 난이도가 높아져서 결국에는 평균적으로 10분이 소요되게 된다.

블록 헤더의 bits는 nonce 값을 계산하는데 기준이 되는 특정 숫자를 나타내며,

블록체인 전체에 걸쳐 일률적으로 적용되는 숫자다.

비트코인 지갑에도 블록 체인의 모든 거래 정보가 저장되나?

비트코인 지갑은 송수금 거래를 가능하게 해주는 클라이언트 소프트웨어이며, 거래 정보를 블록 체인 네트워크에 전파해야 하므로 블록 체인 네트워크의 노드이기도 하다. 하지만, 지갑은 작업 증명 계산을 하지 않기 때문에 블록 체인의 모든 거래 정보를 저장할 필요가 없고, 저장하지도 않는다. 블록 체인 네트워크에 참여하는 노드는 여러 종류가 있으며, 지갑에는 블록 체인의 전체 거래 정보가 저장되지는 않는다.

채굴 보상이 줄어든다면 채굴에 의해 유지되는 블록 체인이 지속될 수 있나?

비트코인을 예로 들면 채굴 보상은 비트코인으로 지급되며, 지급되는 양은 비트코인 기준으로는 줄어들지만 비트코인 자체의 가치가 늘어난다면 보상 자체가 줄어드는 것은 아니다. 몇 년전에 채굴 보상이 50BTC 이고 현재 보상이 12.5BTC로 1/4로 줄었다고 하더라도, 비트코인 자체의 가치는 4배가 훨씬 넘게 증가했기 때문에 실질 보상액은 오히려 늘어났다고 볼 수 있다.

이처럼 디플레이션 화폐라는 비트코인의 특징은 비트코인의 가격을 높이는 중요한 요인이기도 하다. 하지만 신규 발행 비트코인이 0이 되는 시점(약 2100년 이후)에는 어떤 모습일까? 결국 남는 보상은 수수료뿐이므로 수수료를 극대화 하거나 소요 비용을 낮추는 쪽으로 흘러갈 것이다.

결국 블록 사이즈를 키워서 블록 안에 더 많은 거래를 담아서 수수료 수입을 높이거나, 난이도를 낮춰서 채굴 비용을 낮추는 방식 또는 둘의 조합으로 전개될 가능성이 높다.

블록체인에 대한 개념과 사토시 나카모토에 대한 이야기를 할 때 빠지지 않는 코스로 잠깐이라도 비잔틴 장군 문제 혹은 비잔티움 장군 문제, 비잔틴 오류 허용 에 대해 이야기가 나오곤 한다. 그런데 대부분 '아 무언가 신뢰할 수 없는 환경에서 어려운 문제를 풀게 해서 신뢰를 얻어내게 했구나' 정도로 이해하면 다행이고, 설명하는 사람들 조차도 어떤 과정으로 그러한 신뢰를 갖게 하였는지에 대해서는 상세히 모르고 설명하는 케이스가 대부분이었다. 

아래는 PoW

▌두 장군의 문제

불확실한 통신 상황에서 상호 동기화를 시도할 때의 문제점으로,
이건 중간의 통신 장애가 있는 경우 어떻게 공격 시간을 전달할것인가라는 문제이며 아직까지 이에 대한 해결방안은 나오지 않았다.

위 처럼 장군 A1, A2 는 가운데 B 에 있는 적들을 공격해야 하는데, 동시 공격하지 않으면 이길 수 없는 상황이다.
A1, A2 사이를 흐르는 강이 있으나, 건너는 경우 연락병이 포획될 가능성이 있다.
이러한 상황에서, A1, A2 는 시간을 합의하고 상호 확답을 받아야 한다. 

이 문제는 비잔틴 장군의 문제와는 다른 문제이다. 


▌비잔틴 장애허용, 비잔틴 장군 문제

300명의 병력이 있는 비잔틴 성을 100명씩의 병력을 가진 장군 5명이 치려함
이기려면 적 병력보다 많은 병력이 공격해야 함
장군들은 연락병을 보내 소통 가능함
장군들 중에는 배신자가 있어 서로 신뢰 불가함

문제) 서로 신뢰할 수 없는데, 어떻게 공격 시각을 합의할 수 있는가? 
     . 가정 1 - 공격 시각은 상관 없음. 모두 한번에 공격할 수 있는 합의된 시각이면 됨
     . 가정 2 - 배신자는 병력을 분산시키려 하므로 이전 장군의 메시지와 다른 시각을 제시함
     . 가정 3 - 각 장군은 자신의 바로 다음 장군에게만 연락할 수 있음
     . 가정 4 - 배신자도 지정한 시간에 공격에 가담 함


<<문제 생기는 상황>>
  1. @장군1 이 "9 AM" 공격시각을 적어 서명과 함께 @장군2 에게 보냄 
  2. @장군2 는 @장군1 의 "9 AM" 공격시각을 보고 기억한 후 @장군3 에게 전달함
  3. @장군3 은 배신자로, "9 AM" 메시지를 찢어버리고 "8 AM" 으로 고쳐서 @장군 4에게 전달함
  4. @장군4 는 "8AM" 을 기억한 후 @장군5 에게 전달
  5. @장군5 는 "8AM" 을 기억
  6. 8AM 에 @장군3, @장군4, @장군5 가 쳐들어가지만 300명이므로 지게 됨

<<PoW 블록체인 해결>>
[새로운 규칙]
A. 장군은 메시지를 보내기 위해 반드시 10분의 시간을 들여야 함
B. 메시지는 모든 이전 장군의 메시지와 10분의 시간을 들였다는 증거를 포함하여 보내야 함

  1. @장군1 이 "9 AM" 공격시각을 적어 10분간 작업하여 증거와 함께 @장군2 에게 보냄
  2. @장군2 는 "9 AM" 메시지와 @장군1 의 10분 작업 증거를 보고 확신 후, @장군3 에게 "9 AM" 메시지 보냄
    1. @장군2 도 10분 간 작업 함
    2. @장군1, @장군2 의 메시지와 작업내용 모두를 포함하여 보냄
  3. @장군3 은 배신자로 "8AM" 으로 메시지를 수정하여 보내고 싶으나 그냥 보낼 수 없음. 아래 중 택 1 해야 함.
    1. 속일 수 있는 유일한 방법
      1. @장군3 은 10분보다 빠르게 작업을 하여 "8 AM" 메시지를 만들어 냄
      2. @장군1, @장군2 의 총 20분 작업에 해당하는 메시지 모두를 남은 시간 내에 만들어 포함 시켜 보냄
    2. 안들키고 있으려면 "9 AM" 으로 보내기
  4. @장군4, @장군5 모두 동일

[의미]
  1. 만약 @장군3 이 "8AM" 으로 보냈다면, 모두 장군 3이 거짓임을 알게 됨
  2. 만약 @장군2 가 배신자였다면 @장군2,3,4,5 모두 9AM 공격 가므로 이기게 됨
  3. 만약 @장군1 이 배신자였다면 8AM 에 모두 함께 공격 가므로 이기게 됨


비잔틴 장군의 문제를 PoW 를 통한 블록체인 기술로 해결 한 예시이다. 블록체인은 "이전 메시지를 포함" 시킨 새로운 메시지로 이해할 수 있으며, 포함된 이전 메지시를 변경한 경우 변경(위조)되었다는 사실을 바로 알 수 있는 장부이다. PoW 는 "10분의 시간을 들여 메시지를 만드는 과정" 이며, 정말 그러한 작업을 하였는지를 단번에 검증해낼 수 있는 방법을 제공한다.
그러나 10분의 시간을 들이지 않고는 절대 새로운 메시지에 포함된 이전 메시지를 다시 만들어낼 수는 없는 알고리즘이며, 비잔틴 장군의 문제 해결에 가장 핵심이 되는 메커니즘인 것이다. 



출처: http://goodjoon.tistory.com/256 [Good Joon]

컴퓨터의 커널은 운영체제의 핵심입니다. 운영체제의 다른 모든 부분에 여러 기본적인 서비스를 제공합니다. 시스템 자원은 제한되어있지만 프로그램은 많기 때문에 커널은 프로그램의 수행상태인 프로세스 간의 보안 접근을 책임지는 소프트웨어입니다. 커널이 이러한 프로세스마다 얼마만큼의 자원을 사용해야 하는지 결정해야하는데 이것을 스케줄링이라고 합니다. 


같은 종류의 컴포넌트에 대해 하드웨어는 다양하게 디자인 되어질 수 있습니다. 따라서 하드웨어에 직접 접근하는 것은 매우 복잡할 수 있습니다. 일반적으로 커널은 운영체제의 복잡한 내부를 감추고 깔끔하고 일관성 있는 인터페이스를 하드웨어에 제공하기 위해 추상화를 지원합니다. 이러한 하드웨어 추상화는 프로그래머가 하드웨어의 복잡한 접근을 고민할 필요없이 쉽게 개발하는 것을 돕습니다. 하드웨어 추상화 계층(HAL)은 제조사의 장비 명세에 대한 특정한 명령어를 제공하는 소프트웨어 드라이버에 의지합니다.


이렇듯 커널은 운영체제에서 핵심적인 기능을 담당하지만 수행에 필수적인 것만은 아닙니다. 프로그램은 하드웨어 추상화나 운영체제 지원없이 컴퓨터만으로 읽어 들여져 수행될 수 있기 때문입니다. 이러한 방법은 초기 컴퓨터의 운영 방법이었고 다른 프로그램을 실행하고 싶을 때는 컴퓨터는 다시 켜고 다시 읽어들어야 했습니다. 그 결과 로더와 디버거 같은 작은 프로그램들이 프로그램을 수행시키는 작업을 해야했고 이것이 초기 운영체제 커널의 기초가 되었습니다.


이렇게 커널은 크게 4가지로 구분할 수가 있습니다.

- 모놀리식커널(Monolithic Kernel)

- 마이크로커널(Micro Kernel)

- 하이브리드커널(Hybrid Kernel)

- 엑소커널(Exo Kernel)



모놀리식 커널


모놀리식 커널은 하드웨어 위에 고수준의 가상 층을 가지고 있습니다. 고수준의 가상층은 기본 연산 집합과 관리자모드에서 작동하는 프로세스관리, 동시성, 메모리관리 등의 운영체제 서비스 구현을 위한 시스템콜(System Call)로 되어 있습니다.


이러한 연산들을 제공하는 모듈은 같은 주소 공간에서 실행되기 때문에 코드의 집적도는 매우 조밀하고 수정하기 어렵고 한 모듈이 버그는 전체 시스템을 멈추게 할 수도 있습니다. 그러나 구현이 신뢰할 정도로 완성되면 컴포넌트의 내부 집적이 내부의 시스템 이용을 효과적이게 하여 높은 효율을 보입니다. 



모놀리식 커널의 지지자들은 코드가 부정확한지 그런 코드가 커널에 포함되어 있는지 확인할 수 있고 그것은 마이크로 커널에 비해 미세한 우위에 있다고 주장합니다.


리눅스, FreeBSD, 솔라리스와 같은 모놀리식커널은 실행 모듈을 실시간으로 읽어들일 수 있습니다. 실시간으로 실행 모듈을 읽는 특징은 커널이 허용하는 범위 내에서 손쉽게 확장 가능하도록 커널 공간의 코드의 양을 최소한으로 유지시켜줍니다. 


마이크로소프트 윈도우즈 NT(NT, 2000, XP, 2003 등)는 초창기에는 하이브리드커널이었으나 후기버전은 모놀리식커널로 변경되었습니다. 윈도우즈 NT 시리즈는 상위의 서비스들을 NT executive라는 서버로 구현하였습니다. Win32 특성은 처음에는 사용자 모드의 서버 형태로 구현되었으나, 최근 버전에서는 관리자 주소 영역으로 이동하였습니다. 다양한 서버들이 로컬 프로시저 콜(LPC; Local Procedure Call)이라 불리는 주소 영역간 매커니즘을 통해 통신하며, 성능 최적화를 위해 공유메모리를 이용합니다.


모놀리식 커널을 사용한 운영체제는 다음과 같습니다.

- BSD 커널과 같은 전통적인 유닉스 커널

- 리눅스 커널

- 솔라리스 커널

- 윈도우즈 NT 커널

- 벨로나2 커널

- AIX 커널

- AGINX와 같은 교육용 커널



마이크로 커널


마이크로 커널은 모놀리식 커널과 달리 하드웨어 위에 매우 간결한 추상화만을 제공합니다. 기본 연산 집합과 운영체제 서비스를 구현한 쓰레드 관리, 주소 공간, 프로세스간 통신의 작은 시스템 콜로 구성됩니다. 일반적으로 커널이 제공하는 네트워킹 같은 다른 서비스들은 사용자 공간 프로그램인 서버로 구현합니다.


운영체제는 서버를 다른 일반적인 프로그램처럼 간단히 시작하고 끌 수 있습니다. 예를 들어 네트워킹 지원이 필요없는 작은 시스템에서는 간단히 켜지 않으면 됩니다. 이론적으로 마이크로커널에서 시스템은 더 안정적입니다. 서버가 중단될 때 커널의 충돌이 아니기 때문에 단 하나의 프로그ㅐㄻ만 내려버리면 됩니다.


일반적으로 마이크로 커널은 전통적인 디자인의 수행을 잘하지 못할 수도 있습니다. 서버와의 자료교환을 위해 커널을 출입하는 문맥전환 때문입니다. 주의 깊은 조율이 오버헤드를 극적으로 줄여줄 것으로 믿어져왔으나 90년대 중반부터 대부분의 연구자들을 시도를 포기했습니다. 최근에 새 마이크로 커널은 성능을 최우선으로 설계하며 이 문제를 넓은 부분에서 다루었씁니다. 그러나 현재 운영체제 시장은 자기 몸사리며 마이크로 커널 설계에 소극적입니다. 


마이크로 커널에 기반한 운영체제는 다음과 같습니다.

 - AmigaOS

 - Amoeba

 - ChorusOS

 - EROS

 - Haiku

 - K42

 - LSE/OS

 - KeyKOS

 - L4 마이크로커널

 - Mach

 - MERT

 - Minix

 - MorphOS

 - NewOS

 - QNX

 - Phoenix-RTOS

 - RadiOS

 - Spring operating system

 - VSTa

 - Symbian OS



하이브리드 커널


하이브리드 커널은 본질적으로 마이크로 커널을 따르나, 사용자 레벨에서 수행될 때 느린 코드들을 커널 레벨에서 수행하도록 수정한 것을 말합니다.


이는 다양한 운영체제 개발자들이 마이크로커널 기반의 설계를 받아들이던 시점에 순수한 마이크로커널의 성능상 한계를 극복해보고자 생각해낸 내용입니다.




예를 들어, 맥 오에스 텐의 커널인 XNU는 Mach 커널 3.0 마이크로 커널에 기반을 두고 있지만, 전통적인 마이크로 커널 설계의 지연 현상을 줄이기 위해 BSD 커널의 일부 코드들을 들여와 동일한 주소 영역에서 실행하고 있습니다.


하이브리드 커널로는 다음과 같은 것들도 포함됩니다.

 - ReactOS

 - BeOS 커널

 - Netware 커널


하이브리드 커널은 모놀리식 커널과 마이크로 커널 설계 양쪽의 구조적 개념과 작동방법에 대한 것으로 어떤 것은 사용자 공간에 들어가는 반면 어떤 코드는 성능의 이유로 커널 공간에 포함해야 하는지에 대한 선택의 문제입니다.



엑소 커널


엑소커널은 운영체제 설계에 대한 급진적인 신개념으로 수직 구조의 운영체제입니다.


엑소커널의 구상은 개발자에게 강제적인 추상화를 줄여 하드웨어 추상화에 대한 선택의 폭을 넓혀줍니다. 엑소커널은 여러 개의 가상화를 실행하는데 각 가상화는 하드웨어 추상화 계층을 통하지 않고 하드웨어 구역에 직접 접근합니다. 응용소프트에어와 추상화는 특정 메모리 주소와 디스크 블록 등을 요구하는데 커널은 단지 자원이 비어있는지만 확인하고 응용소프트웨어에게 접근을 허용합니다.



이러한 저수주누의 하드웨어 접근은 프로그래머가 개별적인 추상화를 만드는 것을 허용하여 불필요한 부분을 제거할 수 있게 하고 일반적으로 프로그램의 성능을 향상시킵니다.


엑소커널은 추상화를 제공하는 라이브러리 운영체제(libOSes)를 이용합니다. 라이브러리 운영체제는 응용소프트웨어 프로그래머에게 고수준, 전통적인 운영체제 추상화, 맞춤 추상화 구현 등의 더 유동적인 방법을 제공합니다. 이론적으로 엑소커널의 체제는 하나의 엑소커널 아래 윈도우즈나 유닉스와 같은 다양한 운영체제를 구동할 수 있습니다.


엑소커널의 개념은 1994년에 나왔으며 현재까지 여전히 학계에서 연구 중이며 대규모의 상용 운영체제는 아직까지 없습니다.



출처: http://12bme.tistory.com/288 [길은 가면, 뒤에 있다]

+ Recent posts