비트코인 채굴은 프로그래밍적으로 어떻게 되는 것인가?
안녕하세요. 고양이를 좋아하는 Jay입니다.
채굴(mining)하면 뭐가 제일 먼저 떠오르나요? 저는 비트코인에 대해서 잘 모를 때, 광산에서 금을 캐내는 것처럼 비트코인을 채굴하는 이미지에 많이 노출되서 이런 그림이 제일 먼저 떠오르네요. 이번에는 채굴을 어떻게 하는 것인지 프로그래밍적으로 설명해보고자 합니다. 혹시나 저처럼 막연하게 채굴을 금캐는 모습을 상상하는 분이 있다면, 이번 설명으로 조금은 구체적인 그림을 그릴 수 있게 되었으면 좋겠습니다.
제일 먼저 해시함수가 무엇인지 이해할 필요가 있습니다. 간단하게 설명하면 어떤 메세지를 입력하면 해시함수가 이상하게 생긴 문자를 만들어줍니다. 비트코인 블락정보를 보면 아래처럼 Hash라고 이상하게 긴 문자가 있습니다.
비트코인 블록의 정보는 여기서 볼 수 있습니다. 이번블락의 해시값, 머클루트값도 해시함수에 의해서 생성된 문자입니다. 그럼 간단하게 해시함수의 특징들을 살펴보겠습니다.
해시함수
해쉬 함수는 블록체인뿐만 아니라 다른 곳에서 다양한 목적으로 사용됩니다. 여기서는 암호에 사용되는 해시함수 중 비트코인에 사용되고 있는 SHA256 해시함수를 바탕으로 설명하고 있습니다.
해시함수는
1.똑같은 메세지 값을 해시하면 똑같은 결과가 나옵니다.
'hello world!'를 해시하면 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 결과값이 나옵니다.
'hello world!'를 다시 해시하면 동일한 결과값 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 이 나옵니다. 'hello world!'를 해시하면 언제나 동일한 결과값이 나오게 됩니다.
2.결과값으로 어떤 메세지가 해시되었는지 알수가 없다.
7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 결과값만 가지고 'hello world!'이라는 걸 알 수 없습니다. 결과값이 어떤 메세지 값을 가지고 있는지 알 수 있는 방법은 하나씩 메세지 값을 넣어 보는 수밖에 없죠. 즉, 'hello'를 해시해서 결과값과 같은지 확인하고 다르면, 다른 값 'hello jay'를 넣어서 해시해보고, 이것도 아니면 다른 메세지 값을 해시해서 결과값과 같은지 보고, 이렇게 수많은 경우의 수를 하나씩 해보면서 동일한 결과값이 나오길 바래야 합니다.
3.메시지 값을 조금만 바꿔도 해시결과값을 많이 달라진다.
'hello world!'를 해시하면 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9가 나왔습니다. 그럼 글자하나만 대문자로 바꿔서 'Hello world!'를 해시해보면 c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a 많이 달리진 해시결과값이 나옵니다.
4.메세지 길이에 상관없이 똑같은 길이의 해시 결과값이 나옵니다.
'hello world!'를 해시하니깐 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9로 64자리의 문자가 만들어졌죠? 메세지값을 'Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!'라고 해서 해시해도 결과값은 동일한 64자리의 문자 3738d3dfa02f603e849ad63c5aaf54ae64b95a9e837f1485401a8449d3b1cf7f 가 나옵니다. (16진수로 되어 있어서 64자리인데 Sha256에서 힌트를 얻으면 2진수로는 256자리가 됩니다.)
5.다른 두개의 메세지 값이 동일한 해시 결과값을 가지는 것이 거의 불가능하다.
생성된 해시값은 유일한 값이 됩니다. 'hello world'를 해시했더니 A가 나왔는데, 다른 메세지 값인 'welcome to korea'로 해시했더니 동일한 A가 나오는 경우가 없다는 것입니다.
6.메시지값을 해시하여 해시 결과값을 얻는데 큰 연산이 필요하지 않고 빠르게 작동합니다.
그럼 해시함수가 마이닝에 어떻게 활용될까요?
블록의 해더파일이 위에서 설명한 메세지값이 되고, 마이너는 이것을 해시합니다. 'hello world!'를 해시해서 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 값이 나온 것처럼, 해더파일을 해시해서 해시값을 얻는 것입니다.
그럼 375210번째 블록의 정보를 가지고 해시값이 생성되는 과정을 설명해보겠습니다. 먼저 해더는 아래와 같이 구성되어 있습니다. 머클루트에 대해서는 제가 이전 포스팅으로 설명했으니 참고하세요.
해더 종류 | 설명 |
---|---|
버전 | 버전별의 일종의 규칙이 달라집니다 |
이전 블록의 해시값 | 연결된 블록의 해시값 |
해시머클루트 값 | 트랙잭션의 값을 통합해서 가지고 있는 값 |
타임스탬프 | 시간값을 보여줍니다 |
Bits | 난이도값 |
Nonce | 채굴자가 이 값을 하나씩 늘려가서 정답을 찾습니다 |
그럼 이제 375210번째 블록의 정보로 정리해보겠습니다.
버전 : 3
이전 블록의 해시값 : 0000000000000000051f5de334085b92ce27c03888c726c9b2bb78069e55aeb6
해시머클루트 값 : f4db18d3ecab87eeb23a56490d5b0b514848d510d409b43f6bbf2b82f55da8db
타임스탬프 : 2015-09-19 11:59:45
Bits : 403867578
Nonce : 3548193207
이제 이 값들을 다 더해서 메시지 값을 만듭니다. 이 값들은 사이즈(바이트값)에 맞게 그리고 16진수 형식으로 그리고 리틀 엔디언(little endian) 형식으로 나타내야 합니다. 타임스탬프는 Unix Epoch타임으로 변경해줘야 합니다. 그냥 저 값들을 형식에 맞게 바꿔주는 작업이 필요하다는 걸로 이해하시면 될 것 같습니다.
**버전 + 이전블록해시값 + 해시머클루트값 + 타임스탬프 + bits + nonce. 이렇게 값들을 연결해서 긴 메시지 값을 만듭니다. ** 이걸 위에서 설명한 해시함수에 한번 넣어서 해시를 만들고 그걸 다시 해시함수에 넣어서 해시를 만듭니다. 두번해주는거죠. 그럼 짜잔 해시값이 나옵니다.
그럼 채굴자(마이너)는 CPU나 GPU로 엄청나게 전기를 쓰면서 무엇을 하는 것일까요?
간단하게 말하면 채굴자는 해더에 있는 nonce값을 하나씩 변경해가면서 특정 값보다 작은 해시를 찾습니다. 해시함수 특징중에 '2.결과값으로 어떤 메세지가 해시되었는지 알 수 없다.', '3.메시지 값을 조금만 바꿔도 해시결과값이 많이 달라진다.' 기억나시나요? nonce 값을 조금만 바꿔도 해시값을 크게 달라집니다. 그리고 해시결과값을 통해서 역으로 어떤 메세지값을 넣어야되는지 알수가 없으니, 원하는 해시값을 찾기 위해서는 하나씩 넣어서 결과값을 볼 수 밖에 없습니다.
예를 들면, 채굴자는 나머지 해더값에 nonce값을 1로 넣어봐서 해시값을 구해봅니다. 원하는 해시값이 안 나오면, nonce값에 2를 넣어서 다시 해시값을 구해봅니다. 또 원하는 해시값이 안 나오면 다른 값을 넣어봅니다. 이렇게 숫자를 하나씩 변경해가면서 원하는 값이 나올때까지 계속 반복합니다. 컴퓨터가 이렇게 반복하면서 연산을 하는 거죠.
특정 값보다 작은 해시를 찾는 것은 경쟁입니다. 저랑 여러분이 이 해시값을 찾는데 제가 먼저 찾으면 제가 그에 대한 보상으로 비트코인을 받습니다. 홍길동이라는 사람이 먼저 찾으면 그사람이 블록을 생성한거고 그사람이 비트코인을 보상으로 받는 것이죠. 그래서 컴퓨터를 최대한으로 빠르게 돌려서 다른 사람보다 이 해시값을 찾을려고 하는 것이죠.
그럼 특정한 값보다 작은 해시값은 어떻게 결정되는건가요?
495954번째 블록 해시값을 한번 봅시다.
00000000000000000029f9bc75b5e62e12e4dc3387ea5abfe6951519f66e59c3
100000번째 블록 해시값은 어떨까요?
000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506
1000번째 블록 해시값은
00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09
여기서 뭔가 패턴이 보이시나요? 495954번째 블록부터 1000번째 블록으로 갈수록 해시값의 0이 점점 줄어드는게 보이네요. 반대로 블록의 순서(Height) 커질수록 0이 많이진다고 할 수 있죠. 위의 값은 16진수로 되어 있는데 우리가 보기 편한 10진수 값으로 바꾸면 더 확실한 패턴을 볼 수 있습니다.
1000 : 21190640912980581113825823661692185883147471094586368510990797434121
100000 : 1533267872647776902154320487930659211795065581998445848740226310
495954 : 4020457218155086056100726400279037738012423080228313539
뒤에 블록일수록 값이 작아지죠? 숫자가 너무 많이 나와서 좀 머리가 아프실수도 있는데, 숫자의 자릿수가 줄어드는 것만 보세요. 블록이 뒤로 갈 수록 값이 작이지는 것을 볼수가 있습니다.
이제 난이도(Difficulty)랑 연결해서 생각해야 합니다. '작은 값은 난이도가 높다.'
작은 값은 난이도가 높습니다. 채굴자(마이너)가 해시를 하면서 정답을 찾기가 더 어려워진다는 것을 의미합니다. 예를 들어서 설명을 하겠습니다. 우리가 주사위 두개를 던진다고 합시다. 주사위 두개를 던져서 나오는 값을 더한 것이 해시값이라고 해보겠습니다. 그럼 주사위를 던져서 나올수 있는 값의 범위는 2 ~ 12가 되고, 경우 수는 36가지가 되겠죠. 여기서 12보다 작은 값을 찾으라고 하면 6,6이 나오는 경우 빼고 35가지 경우가 다 12보다 작은 값이 될 것입니다. 그럼 7보다 작은 값을 찾으라고 하면 9가지 경우만 해당되겠죠.
12보다 작은 값 : 36가지 중에 35가지만 포함
7보다 작은 값 : 36가지 중에 9가지만 포함
36가지 중에 한 가지를 임의로 선택했을 때 12보다 작은 값은 35/36의 확률로 찾습니다. 한번의 시도로 정답을 찾을 가능성이 높겠죠. 근데 7보다 작은 값은 9/36의 확률로 찾게 될 것입니다. 한번의 시도로 정답을 찾기는 쉽지가 않겠죠. 더 많은 시도를 해서 찾을 가능성이 높습니다.
1000번째 블록의 값은 컸죠? 채굴자가 블록의 해더로 만드는 해시값이 큰 값보다 작으면 되니깐(12보다 작은 값을 찾는 것처럼), 적은 시도로 찾을 가능성이 높겠죠. 10000번째 블록의 값은 작아졌으니깐(7보다 작은 값을 찾는 것처럼), 더 많은 시도를 해서 찾을 가능성이 높을 것입니다.
어느 특정한 값보다 작아야 한다는 것은 해더 값의 bits(난이도 값)에 표시되어 있습니다.
예제로 설명했던 375210번째 블록을 봅시다.
bits는 403867578였습니다. 이 값보다 작은 해시값을 찾아야합니다. bits값을 해당하는 공식에 넣고 똑같은 16진수로 바꿔보면 아래와 같습니다.
그럼 375210번째 블록 해시값만 비교해볼까요? 그냥 단순하게 앞부터 0이 더 많으면 더 작은 값이니깐, 375210번째 블록 해시값이 bits로 계산된 타켓(Target)보다 작은 걸 확인할 수 있습니다. 채굴자들이 nonce값에 하나씩 넣어보면서 타켓(target)값보다 작은 해시값을 찾아야지 정답이 됩니다. 정답을 찾으면 이제 블록을 생성하고 보상으로 비트코인을 받는 거죠.
타켓값 앞의 0 갯수 : 0000000000000000
해시값 앞의 0 갯수 : 00000000000000000
좀 더 자세히 알고 싶은 분들을 위해서 추가적으로 설명하면, 난이도(difficulty)는
으로 되어 있습니다. 현재 타켓 값이 작아질수록 난이도가 증가하겠죠? 나눗셈이 보이니깐 이건 소수값이 됩니다. 그래서 bits는 부동소수값으로 표현되어 있습니다.
난이도 값은 2016블록마다 변경됩니다.
난이도는 20160블록마다 변경이 됩니다. 비트코인에서는 블록하나당 10분을 걸리는 걸 목표로 하니깐, 약 2주에 한번씩 난이도가 조정됩니다. 채굴자(마니어)들이 너무 빨리 찾으면 난이도를 올려야겠죠. 블록하나당 10분을 기준으로 잡았으니깐, 20160블록 * 10분 하면 201600분 걸리는데 기준이 됩니다. 이보다 더 빨리 블록 20160개가 생성되었다면 난이도를 올리고, 이보다 늦게 블록 2016개가 생성되었다면 난이도를 낮추게 되어있습니다.
당연히 채굴하는 장비들이 좋아져서 빨리 정답을 찾을 수 있게 되니깐, 난이도는 img-2의 그래프처럼 계속 증가했습니다.
마무리
여기까지 오느라 고생하셨습니다. 최대한 쉽게 이해할 수 있도록 작성해보려고 노력했는데, 이해가 되셨는지 궁금하네요. 세부적으로 더 설명하고 싶은 점들이 많았는데, 다음 포스팅때 더 설명하도록 하겠습니다.
제가 공부한 내용을 바탕으로 작성하였습니다. 혹시 잘 못 설명된 내용이 있으면 댓글로 알려주세요.
질문 답변
1. 해더의 bits 값을 16진수 혹은 10진수로 변경하는 공식
bits 값 403867578을 가지고 설명을 하겠습니다. 먼저 403867578을 16진수로 바꾸면, 181287BA가 됩니다. 처음 두자리 18와 나머지 1287BA를 나누어서 아래의 식에 대입하면 됩니다. 16진수 18은 10진수로 24가 되고, 16진수 1287BA는 10진수로 1214394가 됩니다.
그럼 이 식은 어떻게 나온 것인지 궁금하실 것 같은데, 1001를 어떻게 bits으로 표현하는 지 예를 들어보겠습니다. 일단 256으로 자르고, 16진수로 표현을 해볼께요. 그럼 1001은 256^1 * 3 + 256^0 * 233이고 16진수로 0x03 * 256^1 + 0xE9 * 256^0이 됩니다.
0x03 * 256^1 + 0xE9 * 256 는 256이 0승부터 1승까지 사용되었으니, bits의 첫 두자리는 02가 됩니다. 그리고 03E9값에 6자리로 채워야하니깐 03E900이 됩니다.
그럼 좀전에 말한 공식에 똑같이 넣어 볼께요.
03E900 => 256256
02 = >2
256256 * 2 ^ (8 * (2 - 3)) = 1001
부동소수점을 어떻게 표현하는지랑 프로그래밍에서 비트 쉬프트를 어떻게 하는지 이해해보면 더 쉽게 이해 될 것 같아요.
일단 256을 베이스로 표현했습니다. 3 * 256^1 + 233 * 256^0. 근데 6자리 맞출려고 03E900이 되었고 이건 3256^2 + 233256^1 + 0*256^0 이 되었습니다. 우린 원래 2라는 걸 알고 있었으니깐, 하나가 더 간걸 알 수 있습니다. 그래서 2^(8 * (2-3)) 즉 256으로 나눠주면 다시 3 * 256^1 + 233 * 256^0 이 됩니다.
조금 복잡한데, 이해가 되셨나요?;;;
2. SHA256(SHA256(headermessage).digest()).digest() 왜 해시를 두번 하는것인지?
저도 이부분에 대해서 고민을 안해보고 bitcoin에서 그렇게 사용한다로만 알고 있었어요. 이렇게 두번 해시하는게 SHA256D라고 불리고, 보안 공격으로부터 좀 더 안전할려고 SHA256D가 사용된다고 합니다. SHA256D가 비트코인에서 블록을 해시하는데 어떻게 이점이 있는건지에 대해서는 좀 더 공부를 해야지 알 수 있을 것 같아요.
Cheer Up!
쉽게 잘 설명해 주셨네요. 완벽하게 이해했습니다. 비트코인을 만든 사토시가 대단한 사람이군요.
오~쉽게 잘 풀어쓰셨네요.잘보구 갑니다.
-> 아마 위의 마지막줄 블록 해시값은 375210번째의 것을 적고 싶으셨던 것 같습니다. 확인해보니 해시값은 아래의 것이고 00000000000000000be983a81043933c38008010b849fd6a35d5dd2d57f929bd
이거랑 중복된 것 같습니다. ^^(제가 이해한게 맞다면요) 상세히 설명해주셔서 공부가 잘되었습니다. 좋은 글 감사합니다~
앗 감사합니다. 네 375210의 해시값을 적는다는게 495954번째 블록 해시값을 적어놓았네요. 수정하였습니다. 자세히 읽어주셔서 감사해요:)
Congratulations @jayground8! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
You made your First Comment
Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here
If you no longer want to receive notifications, reply to this comment with the word
STOP
이해하는데 큰 도움이 되었습니다. 좋은 포스팅 감사합니다! upvote & follow 하고 갑니다 (: 두가지 질문이 있습니다.
=> '해당하는 공식'은 정해져있는건가요?
=> 이것도 연결하는 특정 방식이 있는건가요? 그리고 왜 두 번 하는건지요?
질문해주셔서 감사합니다^^. 포스팅 글 마지막에 질문에 대한 답변을 달아보았습니다. 질문 덕분에 저도 공부가 많이 되었어요~
와... 친절하고 자세한 설명 감사드립니다!!!
블록체인 공부하면서 글을 쓰고 있는데 너무 좋은 글이라 링크를 하려고 합니다.^^
계속 쓰시면 좋겠는데..ㅎㅎ
감사합니다~
감사합니다. 요즘 바쁘고, 제가 아는 내용들을 압축해서 좋은 글을 작성하고 싶은 욕심에 미루다보니 이후에 한 개도 못 올렸네요ㅠ 도움이 되는 글이 되었다니 새해에는 좀 더 많은 컨텐츠 공유할 수 있도록 할께요. 감사합니다^^ 새해 복 많이 받으세요
jayground8님 2018년 새해 소망 릴레이에 추천드렸어요~
https://steemit.com/kr/@feyee95/2018-3
오랜만에 돌아오신 거 같은데 편한 글 하나 쓰시면서 다시 시작하셔도 괜찮을 거 같아서.ㅎㅎ
부담되시면 안해셔도 되는 릴레이니까 편하게 생각하세요~~
새해 복많이 받으시구 행복하시길 바랄게요~~ ^^
덕분에 많은 궁금증이 해결되었습니다.
한 가지 궁금한게 있는데요.
노드가 해쉬를 찾기전에 자신의 블록에 넣어둘 트랜잭션을 쌓아둔 상태에서 해당 트랜잭션 들을 통해서 merkle root를 구하고 해쉬를 찾기 시작한다고 알고있습니다.
여기서 트랜잭션을 쌓아두는 과정에 대해서 자세하게 알 수 있을까요?
Congratulations @jayground8! You received a personal award!
Click here to view your Board of Honor
Do not miss the last post from @steemitboard:
Congratulations @jayground8! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!