프로그래밍/블록체인

[해석서]Bitcoin: A Peer-to-Peer Electronic Cash System - 2. 거래(2/2)

산을좋아한라쯔 2018. 1. 31. 20:48
반응형

이제 해시와 서명에 대해 대략 알았으면, 비트코인의 거래에 대한 설명으로 다시 돌아가겠습니다.

논문에 있는 그림을 보겠습니다.

 

 

 

제일 왼쪽에 있는 거래는, 소유자0 --> 소유자1 입니다. 소유자 0이 소유자 1에게 송금을 하는 것입니다.

두번째 그림은 1-->2, 세번째 그림은 2-->3

 

두번째 그림인 1-->2를 기준으로 해서 보겠습니다.

1은 0로부터 받은 금액을 2에게 보내는 것입니다. 0이 1에 보내는 것은 거래(0 to 1)에 명시되어 있습니다.(제일 왼쪽 그림)

그런데 여기서, 소유자1은 거래(0 to 1)에서 0이 보낸 돈이 자신의 것이라는 것을 증명해야합니다. 

 

즉, 1은 0에게 "돈을 보내시오"하면서 자신의 주소(Addr)을 보냈어야했어고(첫번째 거래인 0 to 1이 발생하기 전에), 0은 그 주소로 돈을 송금했습니다. (이게 첫번째 거래)

그러나, 이 송금을 했다는 것은, 1의 통장이라는 것이 존재해서 거기로 들어오는 개념이 아니라, 그냥 누구에게나 공개되는 거래내역에, "나 0은 1에 해당하는 주소로 얼마얼마 만큼의 금액을 송금했다"라는 거래기록만 남길 뿐입니다. 

따라서, 1은 "0이 보낸 그 1"이 자신이라는 것을 증명해야 합니다. (사실 누구라도 1이 자신임을 증명하기만 하면 그 돈은 자신의 것이 됩니다. 해서, 누군가의 개인키를 알아낼 수 있다면, 그 누군가의 비트코인을 전부 가로챌 수도 있습니다. 혹은, 반대로 보관되어 있는 개인키를 잃어 버리면 자신의 가진 모든 비트코인을 사용할 수 없습니다.)

 

다시 그림을 보면, 첫번째 거래는 0이 1에게 금액을 이전한다는 것이고, 이 때 1을 나타내는데 '1의 공개키'를 사용했습니다. 이제 두번째 거래에서 1은, 첫번째 거래에서 0이 보내준 금액을 가지고, 2에게 금액을 이전합니다. 이 때, 0이 보낸 1이 '나'라는 것을 증명하기 위해 1의 개인키에 의한 서명이 사용되고, 이 금액은 다시 2에게 보내집니다.  (그림에서 2의 공개키로 보내지는 것으로 되어 있는데, 실제는 공개키에 의해 유도된 2의 주소를 사용)

마지막 거래도 보면, 2가 3에게 금액을 이전하는 것인데, 두번째 거래에서 1이 2에게 보낸것을 사용할 수 있게 2의 개인키로 증명하고, 3의 공개키로 금액을 이전합니다.

 

거래 샘플 분석(VM 코드 실행 분석)

거래에 대한 부분을 이해하는게 쉽지 않습니다.

어떻게 특정인의 공개키 주소로 코인을 송금하면 그 특정인이 어떻게 자신이 그 사람임을 증명할 수 있는지, 이러한 거래내역들이 전부 공개되어도 문제없는 지 등.

이러한 부분을 이해하려면, 실제 거래가 일어날 때, 어떻게 동작되는 지 좀 더 파고들 필요가 있습니다.

 

비트코인에서는 거래가 일어날 때, 누구한테 보내는 것이고, 그 누구를 증명하는 작업들이, 프로그램 코드에 의해 수행됩니다. (이러한 부분은 블록체인에서 '스마트 컨트랙'이란 부분으로, 블록체인의 매우 중요한 요소중 하나입니다. 거래가 일어날 때의 조건들을 프로그래밍화해서 오류가 발생하지 않고 약속된 거래가 자동화되어 수행될 수 있게 하는 것입니다.)

 

예를 들어 설명하겠습니다.

거래A, 거래B, 거래C가 수행된다고 가정하겠습니다.

  - 거래A: 소유자0 -> 소유자1

  - 거래B: 소유자1 -> 소유자2

  - 거래C: 소유자2 -> 소유자3

 

 

그림. 거래에 있어서의 잠금/해제 스크립트

 

위 그림에서 처럼, 거래B에서 소유자1은, 거래A에서 소유자0->소유자1로 송금된 코인을, 자신이 받을 권리가 있다는 것을 증명해야합니다. 즉, 소유자0가 소유자1의 공개키주소로 보냈는데, 그 공개키주소가 자신의 것이라는 것을 증명하는 것이지요. 증명 방법은 위 쪽에서 설명했듯이 서명/검증 메카니즘에 의해 이루어집니다. 그리고, 그 메카니즘은 스크립트로 프로그래밍화되어 있습니다.

 

위 그림에서, 거래B의 소유자1과 연결되어 있는 스크립트는 '해제 스크립트'입니다. 이름에서 알 수 있듯이, 누군가가 제약을 걸어놓은 것을 해제하는 것으로, 여기서는 거래A에서 소유자0->소유자1로 보낼때 적어놓은 '잠금 스크립트'에 의한 제약을 해제하는 것입니다.

스크립트의 내용을 보면,

 

해제 스크립트

  <Sig>  <PubKey> 

 

잠금 스크립트 

  OP_DUP   OP_HASH160   <PubkeyHash>   OP_EQUALVERIFY   OP_CHECKSIG

 

스크립트만 봐서는 어떻게 동작되는 지 잘 모를것입니다. 그러나, 직접 한단계씩 실행되는 것을 따라가 보면, 대강 이해가 될 것입니다.

해당 스크립트가 하나씩 실행될 때, 어떻게 되는 지 보겠습니다.

 

이 스크립트는 스택베이스로 동작합니다. (비트코인 시스템에서 그렇게 정의한 것임) 

 

스택이라는 것은 LIFO(Last In - First Out) 즉, 가장 먼저 최근에 들어온 값이 제일 먼저 나가는 데이터구조입니다. 접시를 하나씩 쌓는 모습을 상상하면 이해가 쉬운데, 접시가 주어지면 하나씩 쌓고, 접시를 뺄때는 제일 위에 있는 접시를 뺀다고 생각하면 됩니다. 이 때 접시를 데이터라고 생각하면, 제일 최근에 들어온 데이터는 가장 위에 있을 것이고, 데이터를 빼낼 때는 제일 위 쪽에 있는 데이터를 하나씩 빼는 것이기에, 최근 데이터가 가장 먼저 빠져나가게(LIFO) 되는 것입니다.

  

시작은 거래B의 해제 스크립트부터 이루어 집니다. 거래B에서 소유자1이, 거래A에서 이루어져있던 소유자0->소유자1에 대한 '잠금'을 해제시키려는 것입니다.

 

명령어 수행 후 
스택 상태
설명
<Sig>   - 1->2 거래에 대해 해시 계산 후, 그 해시에 대해 1의 개인키 이용해서 sign생성  
  sig - 계산된 서명값 sig 값이 스택에 push 됨      
<PubKey>   - 1의 공개키(PubKey)를 스택에 push        
  PubKey_1              
  sig              

거래B는 소유자1이 소유자2에게 송금하는 것입니다. 여기에 사용되는 금액은 거래A에서 소유자0이 소유자1에게 보낸 금액을 이용할 것입니다. 따라서 거래A에서 자신(소유자1)에게로 송금된 금액이 자신 것이라는 것을 증명해야합니다. 그 증명을 하는 것이 위 스크립트입니다. 

먼저 이번 거래(거래B: 소유자1->소유자2)에 대해서 자신(소유자1)의 개인키로 서명을 생성하고 그 값을 스택에 올립니다. 그리고, 자신의 공개키를 스택에 올립니다. 

이제 해제할 준비는 되었고, 거래A의 잠금스크립트가 실행됩니다.

 

명령어 수행 후 
스택 상태
설명
<Sig>   - 1->2 거래에 대해 해시 계산 후, 그 해시에 대해 1의 개인키 이용해서 sign생성  
  sig - 계산된 서명값인 sig값이 스택에 push 됨      
<PubKey>   - 1의 공개키(PubKey)를 스택에 push        
  PubKey_1              
  sig              
OP_DUP   - OP_DUP 명령어는, 제일 위에 있는 스택값을 복사해서 스택에 push하는 것.  
  PubKey_1 - 따라서, 제일 위에 있는 PubKey값이 복사되어 스택에 쌓인다.    
  PubKey_1              
  sig              
OP_HASH160   - OPHASH160은, 스택 제일위에 있는 값에 대해, 해시값을 계산 한다. (double hash)
  H_PubKey_1 - 따라서, 제일 위에 있는 PubKey에 대해 RIPEMD160(Sha256(PubKey)) 수행하고,  
  PubKey_1    그 결과를 스택에 쌓는다.          
  sig - 이 값은 사실, 1의 주소를 1의 공개키로부터 유도하는 것이다.    
<PubKeyHash> PubKeyHash_1' - 0->1 거래에서 0이 수신인으로 넣은 1의 주소값이다.  
  H_PubKey_1 - 즉, 이 주소를 유도해낼 수 있는 공개키를 가진사람이면, 수신자로서 자격을 획득   
  PubKey_1   하게 된다.            
  sig              
OP_EQUALVERIFY   - OP_EQUALVERIFY는, 스택 제일 위 값과, 그 밑의 값을 서로 비교하라는 명령어  
    - 만약 같은면, 스택 제일 위값과 두번째 값을 pop하고, 그 다음 명령수행  
  PubKey_1 - 만약 다르아면, fail 처리하고, 스크립트 종료      
  sig - 여기서는, 1이 제대로 공개키를 제시했다면, ok가 뜰 것임    
OP_CHECKSIG   - OP_CHECKSIG는, 스택 제일 위에 있는 공개키를 이용해서, 그 밑 스택에 있는  
      서명값에 대해 검증하라는 명령어        
    - 제대로된 개인키-공개키 쌍이면, 해당 개인키로 서명된 것이기에, 검증 성공할것임
                 

 

중간에 OP_HASH160은, 스택이 제일 위에 있는 값에대한 해시를 계산하라는 명령어입니다. 현재 제일 위에는 1의 공개키가 있으므로, 이것에 대한 해시값을 계산하면, 1의 주소가 됩니다. 

이 다음 스크립트명령어인, <PubKeyHash>에 의해 현재거래(이 스크립트는 잠금 스크립트이고 거래A에 존재)에서 소유자0이 송금대상자로 지정한 주소값이 스택에 push 됩니다. 이제 스택에는, 제일 위에는 거래A에서 수신인으로 지정한 주소가, 그 밑에는 소유자1의 주소가 들어가 있는 상태가 되었습니다. 이 두개를 비교하면 될 것이고, 그 다음 명령어인 OP_EQUALVERIFY에 의해 비교됩니다.

 

이제, 거래A에서 수신인으로 지정한 주소가 소유자1이라는 것은 증명이되었고, 남은 것은 이 금액을 사용할 소유자1 자신이 이 거래를 요청한다는 것을 증명해야합니다. 이 증명은 자신만이 소유하는 개인키를 가지고 증명하게 됩니다. 즉, 소유자1만이 할 수 있는 '서명'을 생성하면 되는 것입니다. 이는 이미 '해제 스크립트'에서 소유자1이 자신의 개인키로 서명을 생성한바 있습니다. 그 값이 스택의 제일 밑에 남아있고, 스택의 제일 위에 있는 소유자1의 공개키로 서명검증을 해보게 함으로써, 소유자1이 거래A에서 발생하여 자신에게 송금된 금액을 사용하는 거래가 완료됩니다.

 

앞에서 살펴본 바와 같이, 양자간의 거래에 있어, 송금하는 사람이 진짜 자신이 보냈다는 증명과, 받는 주소로 되어 있는 사람이 자신이라는 각각의 증명이 가능함을 봤습니다. 즉, 보냈다는 서명이 있기에 보내는 사람은 그 거래에 대해서 부정할 수 없고(부인 방지), 받는 사람은 자신이 가지고 있는 개인키를 이용해서 그 거래의 수신인이 자신임을 입증할 수 있었습니다. 

 

이렇게 하나의 거래에대해서는 그 완전성이 보장되었습니다. 보내는 사람이 부정할 수 없고, 받는 사람이 자신이라는 증명이 완벽하게 이루어집니다. 그럼 다 되는 걸까요? 남아있는 문제가 있습니다. 이중사용(Double Spending) 문제입니다. 

예를 들어 보내는 사람이, 동일한 100코인을 가지고 B에도 보내는 거래를 만들고, C에도 보내는 거래를 만들었다면 어떻게 될까요? 각각의 거래 단독으로만 보면 완전할 것입니다. 보내는 사람이 서명하고, 받는 사람인 B와 C가 자신이 받는 사람임만 증명하기 때문에. 그러나, 전체로 보면, 그 사람은 동일 100코인을 가지고 두 번 사용하는 사기를 저지른 것입니다. 이러한 문제를 Double Spending이라합니다.

 

이 이중사용문제가 큰 난제였는데, 이 논문에서 그 문제를 해결할 방안을 제시하고 있습니다. 논문 내용을 보겠습니다. 

 

이 과정상 문제는, 대금을 송금한 소유자가 해당 코인을 중복으로 사용하지 않았다는 것을 수금 자(=받는 이)가 확인할 수 없다는 데 있다. 이 문제를 해결하는 일반적인 방법은 중앙의 신뢰기관 혹은 조폐국(造幣局)을 두고, 거래마다 이중 지불인지 여부를 확인하는 것이다. 거래가 끝난 후에 해당 코인이 조폐국으로 회수되어 다시 발행되게 하고, 조폐국에서 직접 발행된 코인만이 이중 지불되지 않는 코인으로써 신뢰성을 갖게 하는 것이다. 이러한 해결책의 문제점은, 모든 거래가 조폐국을 운영하는 회사를 거쳐 가게 되고, 전체 통화체계의 운명이 마치 은행과 유사한 이 운영회사에 달려있다는 것이다.

    결국, 먼젓번 소유자가 현재의 거래 이전에 어떤 거래에도 서명하지 않았음을, 수금자가 알게 할 방법이 필요하다. 이를 위해서는, 현재의 거래가 가장 첫 번째 거래라는 것만 입증되면 되고, 그 이후에 이중 지불이 시도되는지는 상관없다. 거래의 앞에 다른 거래가 없었다는 것을 확인하는 유일한 방법은 모든 거래 내역을 인지하는 방법밖에 없다. 조폐국 기반의 모델에서는, 조폐국이 모든 거래에 대해 인지하면서 어떤 거래가 가장 처음 발생한 것이지 판단했었다. 이러한 것을 신뢰기관을 이용하지 않고 가능하게 하려면, 거래들이 공개되어야 하고[1], 거래에 참여하는 참가자들이, 그들이 받은 순서의 단일 이력에 합의하는 시스템이 필요하다. 수금인들에게는 거래 때마다, 대다수의 노드가 이 거래가 처음 거래라고 합의한 증거가 필요하다.

 

디지털화폐시스템에서 이중지불을 막는 쉬운 방법은, 모든 거래에 중앙통제기관을 개입시키는 것입니다. A라는 송금자가 중앙통제기관에 기록되어 있는 100코인을 B에게 보내면, A 소유로 되어 있던 100코인을 삭제하고, B에게 새로운 100코인을 생성하게 하는 것입니다. 그렇게 되면 A가 가지고 있는 코인이 없으므로, A가 다시 C에게 이중으로 보낼 수 없게 됩니다. 

 

중앙통제기관없이 이중지불을 막을 수 있는 방법은 없을까요? 

이 논문에서는 가능하다고 합니다. 거래원장이 모둔 사용자에게 공개되고, 사용자들로 구성된 노드들이 거래의 순서에 대해 검증해주면된다고 합니다. 

이러한 아이디어가 바로 블록체인의 원리입니다. 이제 하나씩 하나씩 어떻게 거래에 대한 이중지불을 막을 수 있는 지 설명합니다.

 

다음장 3. 타임스탬프 서버

-계속-

 

[전체 목차]

1. 서론  
2. 거래(1/2)  
2. 거래 (2/2)
3. 타임스탬프 서버 
4. 작업 증명 
5. 네트워크 
6. 인센티브 
7. 디스크공간 회수 
8. 지불 입증 간소화 
9. 금액의 결합과 분할 
반응형