이제부터 비트코인과 블록체인의 기술적인 내용을 다루려고 한다.
블록체인은 이름 그대로 여러 블록들이 서로서로 고리를 물고 체인처럼 연결된 구조를 하고 있다.
이 구조로 어떻게 탈중앙화된 화폐를 만들 수 있을까?
우선 비트코인에는 '트랜잭션' 이라는 개념이 존재한다.
이 트랜잭션은 비트코인을 사용해서 발생하는 모든 거래 정보를 나타낸다.
Alice가 Bob에게 1비트코인을 지불하고 상품을 받는 거래를 한다고 한다면,
'엘리스가 밥에게 1비트코인을 지불한다' 라는 행위가 트랜잭션이 되는 것이다.
이때 비트코인에서 발생하는 모든 트랜잭션에는 '서명'이 필요하다.
비트코인은 Permissionless 블록체인이라 누구나 접근할 수 있는 데이터베이스기 때문에,
이 트랜잭션이 '내가' 발생시킨 트랜잭션임을 확실하게 서명을 통해 나타내는 것이다.
그러면 다른 사람들이 이 트랜잭션이 유효한 트랜잭션임을 인정해주고, 진짜 발생한 거래 내역으로 포함된다.
해시 함수
비트코인은 다른 말로 '암호화폐' 라고도 부른다.
그 이유는 비트코인이 여러가지 암호화 기술로 구성되어 있기 때문이다.
이때 블록체인에서 주로 사용하는 암호화 기술을 이해하기 위해 먼저 '해시 함수' 에 대해 정리해본다.
해시 함수는 어떤 키가 주어졌을 때, 다른 값으로는 만들 수 없는, 오로지 그 값으로만 만들어지는 특정 값을 만드는 함수이자,
결과물 값을 보고 기존의 키 값을 알아낼 수 없는 함수를 말한다.
보통 파이썬에서 사용하는 딕셔너리와 같은 자료구조는 '해시 테이블' 자료구조로 되어있는데, 이때 해시 함수가 사용된다.
해시 함수는 어떤 크기의 input 이 들어와도 항상 같은 크기의 output을 내보내는 특징이 있다.
예를 들어 256 bit 해시 함수라면 어떤 입력이 들어와도 항상 256 bit 의 결과를 내보내는 것이다.
따라서 1페타바이트의 매우 큰 인풋을 주더라도 이 해시 함수를 거치면 256 bit 가 결과로 나온다.
비트코인에서는 sha 256 과 같은 해시 알고리즘을 사용하는데, 이 해시 함수가 바로 항상 256 비트의 결과를 내보내는 함수이다.
이런 해시 함수는 우리도 간단하게 구현할 수 있다.
예를 들면 데이터를 균등하게 256 bit 씩 자른다음, 각각의 조각을 모두 더하는 것이다.
하지만 암호 화폐에서는 일반적인 해시 기능에 암호학적 성질이 더해진 'Crypto Hash Function'를 사용한다.
Cryptographic Hash Functions 특징
1. General Properties
크립토 해시 함수도 일반적인 해시 함수의 특징을 그대로 갖는다.
- 어떤 사이즈의 인풋 x가 들어와도 고정된 크기의 아웃풋을 내보낸다.
- 언제나 같은 x에 대해서는 같은 아웃풋을 내보내는 deterministic 한 성질을 갖는다.
- 아웃풋을 계산하는 것은 매우 효율적인 계산으로 빠르게 이루어진다.
2. Cryptographic Properties
여기에 암호학적 성질이 더해지면 크립토 해시 함수가 된다.
- Preimage resistant (One Way)
output이 주어졌을 때 역으로 input x를 쉽게 알아낼 수 없다.
크립토 해시 함수의 매우 중요한 특징으로, 이 특징이 깨지면 암호화폐는 존재할 수 없게 된다.
지금 암호화폐가 사용가능한 이유는 수학자들이 계산했을 때 역으로 input을 알아내려면 1000년 정도 걸릴 것이라고 예측하기 때문에 사용할 수 있는 것이다.
- Collision Resistant
어떤 해시 함수 H 에 대해, H(x) = H(y) 를 만족하는 x, y 를 찾는 것이 매우 어렵다.
위와 같이 서로 다른 인풋에 대해 같은 아웃풋이 발생하는 경우를 해시 충돌이라고 하는데,
해시 충돌이 일어나도록 하는 두 값을 알아내는 것이 매우 어렵다.
이 특징도 매우 중요한 특징이다.
- Avalanche effect
인풋 값이 조금만 변해도 아웃풋이 크게 변한다.
크립토 해시 함수와 대칭키 암호화에서 유용하게 사용되는 성질이다.
- Puzzle friendliness
인풋값의 일부와 아웃풋을 알고 있을 때, 그로부터 나머지 인풋 값을 알아내는 것이 매우 힘들다.
예를 들어 어떤 100MB 의 인풋 중 256bit 를 제외한 나머지 값을 알고 있다고 해보자.
이때 내가 알고 있는 output 값을 도출해내기 위한 나머지 값을 알아내려면, 비록 99%의 값을 이미 알고 있음에도 나머지 모든 경우의 수 (2의 256승) 를 다 해보는 수 밖에 없다.
크립토 해시함수는 비트코인과 블록체인에서 매우 중요하게 사용된다.
비트코인은 이 성질을 이용해서, 채굴자들로 하여금 무수히 많은 해시 계산을 하도록 시킨다.
그리고 2009년부터 지금까지 15년이 넘는 기간동안 무수히 많은 해시 계산을 하지 않고도 채굴할 수 있는 방법은 아무도 찾지 못했다.
또한 해시 함수는 어떤 데이터에 대해 지문역할을 한다.
매우 큰 데이터 x에 대해 그 데이터의 해시값 H(x) 는 다른 어떤 데이터를 넣어도 쉽게 충돌하지 않으므로
이 H(x) 값을 x의 지문처럼 쓸 수 있는 것이다.
누군가 이 해시 값을 아주 조금만 바꾸어도 기존 x를 해싱한 값과 달라지므로 변경을 감지할 수 있다.
예를 들면 대학교의 성적 데이터는 졸업한 이후에도 그 값이 변경되지 않았음을 보장하는 무결성이 매우 중요하다.
그래서 성적 데이터 자체 외에도, 그 데이터를 해싱한 값을 함께 백업해서 보관한다.
따라서 누군가 성적 데이터를 일부 수정해버린다면, 주기적으로 백업한 해싱값과 비교할 때 변경 여부를 알아낼 수 있게 된다.
해시가 디지털 지문의 역할을 한다는 것을 보여주는 예시로 위와 같은 예시도 있다.
이 그림은 뉴욕 타임즈 일간지에 실린 어떤 해시값이다.
정확히는 해시 값을 base64로 인코딩한 값이다. (해시값은 0과 1의 값이므로 인코딩을 해서 나타낼 수 있다.)
base64 인코딩은 말 그대로 6bit 씩 데이터를 묶어서 64가지 문자로 표현하는 인코딩 방법을 말한다.
알파벳 52글자 + 0부터 9까지 10개 숫자 + (+, =) 기호 2개 해서 총 64개의 문자를 사용한다.
이렇게 해시값을 신문에 박제해두었기 때문에, 이 박제한 데이터에 대해서 나중에 내가 이 원본 데이터를 만든 사람이라는 것을 그 해시값을 만들어내는 원본 데이터를 제시함으로서 증명할 수 있게 된다.
비트 코인에서의 활용
사카시 나카모토는 크립토 해시 함수를 2가지 용도로 사용하도록 비트코인을 설계했다.
첫 번째는 Pointer 이고, 두 번째는 Commitments 이다.
포인터로서의 역할로는, 블록체인에서 블록과 블록의 연결을 가리키는 포인터 역할에 해시값이 사용된다.
Commitments 로서의 역할로는, 어떤 블록체인의 데이터에 대해 이 데이터가 조작되지 않았음을 확인하는데 사용될 수 있다.
블록체인에서는 모든 블록이 전세계에 뿔뿔이 흩어져있으므로, 하나의 데이터와 연결된 모든 데이터를 한번에 바꾸지 않는 한 데이터를 조작할 수 없다.
비트코인에는 Merkle Tree 와 Header 라는 개념이 있는데, 여기에 SHA256 해시 함수를 사용한다.
또 비트코인의 주소 (계좌번호) 를 계산하는 데에도 SHA256과 RIPEMD160 해시 함수를 사용한다.
지금은 SHA 256 보다 더 안전한 것으로 알려진 해시 함수가 등장했지만, 해시 함수를 변경할 수는 없다.
지금까지 채굴한 모든 블록은 SHA 256 방식으로 계산했기 때문에, 이후에 새로운 해시 함수로 계산한 값과는 맞지 않기 때문이다.
(이를 카리켜 Backward Compatability 가 성립하지 않는다고 한다.)
블록체인의 구조
비트코인의 블록체인은 위와 같은 구조로 되어있다.
파란색 큰 네모가 하나의 블록이고, 이 블록들이 서로 검은색 선으로 연결되어 체인이 형성된 모습을 하고 있다.
시간이 자남에 따라 새로운 블록이 생겨나고(채굴되고) 이전 블록에 연결되면서 체인의 길이가 점점 길어진다.
각각의 블록들은 블록 헤더를 갖고 있으며, 그 헤더 안에는 이전 블록 헤더의 해시값을 포함하여 갖고 있다.
이 해시값이 일종의 포인터가 되어 이전 블록과의 연결고리로 기능하는 동시에,
이 해시값은 그 블록에 대한 Commitments 를 나타내어 해당 블록을 누구도 조작할 수 없게 된다.
누군가 만약 내용을 바꿔치는 경우, 그 블록에서 계산된 해시값이 변경되어 연결고리가 끊어지기 때문이다.
블록 헤더
블록 헤더에는 다음과 같은 값들이 들어간다.
- version : 블록을 만들 때 사용하는 소프트웨어의 버전
- 이전 블록 헤더의 해시 값
- Merkle Root Hash : 비트코인의 블록들은 사실 '트랜잭션'을 포함하는 것이 목적이다.
트랜잭션은 비트코인의 계좌이체 명령과 같은데, 한 비트코인 어드레스에서 다른 비트코인 어드레스로 얼마의 비트코인을 보내라는 명령이 트랜잭션이다.
이때 트랜잭션들은 Merkle Tree 라는 일종의 바이너리 트리 자료구조를 통해 저장되는데, 그 트리의 루트에 대한 해시값을 저장한다.
- 타임스탬프 : 이 블록을 생성한 시간
- Difficulty Target : 비트코인(블록)의 채굴 난이도
비트코인 시스템은 블록을 채굴하면 채굴한 블록에 들어있는 모든 트랙잭션 수수료의 합과, 추가적은 채굴 보상을 비트코인으로 받는다.
이때 비트코인의 가격이 올라서 많은 사람들이 채굴에 뛰어들면 많은 비트코인이 채굴되어 가치가 하락하므로, 이를 방지하기 위해 채굴 난이도를 알아서 조절하는 시스템을 만들어두었다.
블록 헤더에 저장되는 채굴 난이도는 이 블록이 채굴될 당시, 모든 채굴자가 인정하는 그 당시 프로그램이 결정한 난이도가 저장된다.
- Nonce : 이 블록을 채굴하기 위해 찾아야 했던 값
비트코인을 채굴하는 것은, 수많은 해시 연산을 통해 이 Nonce 값을 찾는 것과 같다.
넌스는 암호학에서 사용하는 용어로, Number used ONCE 의 줄임말이며, 1번 사용된 숫자, 즉 일회성 숫자를 말한다.
채굴자는 이 넌스값을 계속 바꾸면서 이 넌스를 포함한 해시 연산의 결과가 difficulty target 안에 들어가는지를 체크한다.
어떤 넌스값이 difficulty target 안에 들어가는지는 계산을 통해 알아낼 수 없음을 암호학자들이 보장하므로, Difficulty Target 안에 들어가는 넌스값은 가능한 모든 경우를 다 시도해보는 수 밖에 없다. 그래서 비트코인 채굴은 많은 컴퓨팅 파워를 필요로 한다.
Merkle Tree
merkle tree 는 위 그림과 같이 생겼다.
트리 아래쪽 leaf node를 데이터가 가리키는데, 이 데이터들이 트랜잭션이라고 생각하면 된다.
그리고 특히 이 데이터들 중 제일 왼쪽에 있는 데이터를 '코인 베이스 트랜잭션' 이라고 한다.
코인 베이스 트랜잭션은 이 블록을 채굴했을 때 받아가는 채굴 보상을 나타내는 트랜잭션이다.
(위에서 말했듯 블록을 채굴하면 그 블록에 포함된 모든 트랜잭션의 수수료와 더불어 추가적인 비트코인 보상을 받는다고 했는데, 추가적인 비트코인 보상에 대한 트랜잭션이 코인 베이스 트랜잭션이다.)
merkle 트리는 각각의 데이터로부터 계산한 해시값에 대해, 다시 그 해시값 2개를 이어붙인 값을 해싱한 해시값을 연쇄적으로 계산하여 부모 노드로 둔다.
이때의 해시 함수는 크립토 해시함수이기 때문에 인풋 값의 크기에 제한이 없다. 따라서 얼마든지 해시 결과를 이어붙인 값도 해싱할 수 있다.
이렇게 계산하면 결국 하나의 노드가 남게되고, 이렇게 남은 노드가 merkle tree 의 루트노드가 된다.
비트코인을 채굴하기 위한 해시 퍼즐을 풀 때는, 이렇게 모든 트랜잭션의 해시를 연쇄적으로 해싱한 merkle tree 의 루트노드 값을 포함해서 해시 퍼즐을 풀게 된다.
이때 채굴할 때 내가 원하는 트랜잭션들을 골라서 포함시킨 뒤 퍼즐을 풀 수 있는데, 만약 중간에 특정 트랜잭션을 버리거나 수정하거나 추가하고 싶은 경우, 다시 merkle tree를 순차적으로 계산해서 root hash 값을 구하고, 이 값으로 다시 퍼즐을 풀어 nonce 값을 구하면 된다.
'CS > 블록체인' 카테고리의 다른 글
[블록체인] 6. Proof-of-Work (Consensus Protocol & Native Currency) (0) | 2024.10.11 |
---|---|
[블록체인] 5. 트랜잭션 포맷 (0) | 2024.10.10 |
[블록체인] 4. 디지털 서명과 비트코인 주소 (0) | 2024.10.10 |
[블록체인] 2. 블록체인의 활용 (0) | 2024.10.09 |
[블록체인] 1. 블록체인 개요 (0) | 2024.09.30 |