비트코인의 스마트 컨트랙트

허정·2022년 3월 10일
0

블록체인

목록 보기
22/38

1. UTXO 동작 원리

비트코인은 특정 지갑에 저장되는 것이 아니라, UTXO에 저장되며, 이 UTXO를 통해 코인의 존재 여부를 확인할 수 있습니다.

(1) Transaction Version

네트워크에서 트랜잭션의 유형을 지정하는 버전 번호입니다. 트랜잭션은 버전 별로 구조와 규칙이 다릅니다. 따라서 노드는 트랜잭션을 읽을 때, 버전 번호를 확인하고 트랜잭션을 어떻게 읽어야 하는지 파악할 수 있습니다.

(2) Lock Time

트랜잭션을 블록체인에 바로 포함시킬 수 있는지, 아니면 지정된 시간이 지나면 포함시킬 수 있는지를 지정합니다.

(3) Inputs / Outputs

트랜잭션의 출력은 지폐를 사용하는 것과 같습니다. 잔돈 개념을 사용합니다. 출력은 Lock과 Value로 구성되어 있습니다. 출력의 Default는Locked입니다. 입력에는 잠금을 해제할 수 있는 Key가 들어있습니다. Value는 출력 내에 잠겨있는 사토시의 양을 의미합니다.

트랜잭션의 입력은 우리가 지불한 치킨값 2만원과 비슷합니다. Pointer와 Unlocking Key가 들어있습니다. Pointer는 이전 트랜잭션 출력을 가리킵니다. Unlocking Key는 이전 출력을 해제하는 데 사용됩니다. 5만원에서 2만원을 지불하고, 남은 3만원은 돈을 지불한 사람에게 돌려주어야 하기 때문에, 새로운 출력을 만듭니다.

정리하자면, 출력에는 자산이 잠겨있고, 입력으로 출력을 해제하여 출력에 있는 값을 꺼내, 새로운 출력에 자산을 담습니다. 트랜잭션에는 다른 곳에 잠겨있는 출력을 해제하는 입력과, 입력의 결과로 생긴 새로운 출력이 담기게 됩니다. 따라서, 트랜잭션은 이전 출력을 해제하고, 새로운 출력을 만드는 추상적인 액션이라고 볼 수 있습니다. 이렇게 입력에 의해 생성된 후, 다른 입력에 의해 해제되지 않은 트랜잭션 출력을 UTXO라고 합니다.

UTXO는 잠겨있는 비트코인 그 이상도 그 이하도 아닙니다. UTXO는 코인을 담고 있는 상자이며, 누군가에게 코인을 전달할 때, UTXO 상자에 코인을 모두 꺼내고 새로운 상자에 지불해야할 코인을 담아 전송하고, 다른 새로운 상자에는 남은 코인을 저장합니다.
자동차 가격이 6 BTC인 경우를 생각해봅시다. 트랜잭션은 입력을 통해 우선 10 BTC가 든 출력을 해제한 후에 두 가지 새로운 출력을 만듭니다. 하나는 밥에게 지불되어야 할 6 BTC가 든 출력이고, 다른 하나는 앨리스에게 거슬러줄 4BTC가 든 출력입니다. 이 새로운 출력들은 아직 다른 입력에 의해 해제되지 않았기 때문에 UTXO(미사용 트랜잭션의 출력값)가 됩니다.
UTXO 모델은 확장성이 좋다는 장점이 있습니다. 트랜잭션 로직이 매우 단순하기 때문에 병렬적으로 트랜잭션을 검증할 수 있습니다.

2. 비트코인 스크립트

비트코인에서는 Script라는 스크립트 언어를 사용해 스마트 컨트랙트를 구현합니다. 일반적으로 비트코인에서 사용하는 스크립트는 비트코인 스크립트라고 부릅니다. 스크립트는 트랜잭션에 연결되어 있습니다. 네트워크의 모든 노드는 트랜잭션을 받을 때마다 자신의 로컬 컴퓨터에서 트랜잭션에 연결된스크립트를 실행하며 이를 통해 비트코인의 송금이 이루어집니다. 이러한 특징으로 인해 비트코인은 프로그래밍 가능한 화폐(Programmable Money)에 대한 대중성을 부여했다고 볼 수 있습니다.

노드는 피어로부터 트랜잭션을 받으면 먼저 해당 트랜잭션 안에 들어있는 입력과 출력 목록에서 각 입력과 출력에 해당하는 ScriptSig와 ScriptPubkeys를 추출합니다. 그리고 기존 블록들을 찾아보며 입력과 연결된 이전 출력을 찾고, 각 입력과 출력에 들어있는 ScriptSig와 ScriptPubkey를 연결시킵니다. ScriptPubkey의 소유자(해당 공개키의 소유자)만이 ScriptSig를 만들 수 있습니다.

스크립트는 스택 구조로 작동합니다. First In Last Out을 따릅니다. 이 스택에서 데이터가 들어오는 Push, 데이터가 나가는 Pop이 작동합니다. 스크립트 프로그램은 두 종류의 객체를 가지고 있습니다.

  • Opcode: 덧셈, 뺄셈, 곱셈과 같은 연산 작업을 나타냅니다.
  • 데이터: OP_CODE가 아닌 모든 데이터는 원시 데이터로 해석되며, 스택에 들어가게 됩니다.

스크립트는 이 Opcode와 Data를 일렬로 늘어놓은 것입니다. 여기서 Pointer는 일렬로 늘어진 Opcode와 데이터를 순서대로 하나씩 가리킵니다. 만약 포인터가 데이터를 가리키면 데이터를 스택에 넣고, Opcode를 가리키면 스택에서 데이터를 꺼내옵니다. Opcode는 스택에서 데이터를 하나 이상 가져올 수 있지만, 중요한 것은 스택 구조이기 때문에 가장 나중에 들어온 데이터부터 가져온다는 것입니다.

스크립트 실행이 성공적이면, 스택의 가장 상단에 있는 요소는 1이 됩니다. 만약 스크립트를 끝까지 실행했는데도 스택 최상단에 1이 아닌 다른 값이 들어있다면 스크립트 실행을 실패한 것으로 간주합니다.

노드가 네트워크로부터 새로운 트랜잭션을 받으면, ScriptSig와 ScriptPubkey 필드를 추출하여 연결하고, 최종적으로 <ScriptSig.><SriptPubkey.> 형태의 스크립트를 얻게 됩니다. 노드는 이 스크립트와 빈 스택 하나를 사용해 스크립트를 실행하고, 실행이 완료되면 최상위 스택 요소가 1인지 확인합니다. 1이면 트랜잭션이 유효하다고 간주하고 노드는 트랜잭션을 주변 노드들에게 전파합니다. 만약 트랜잭션이 유효하지 않은 경우 트랜잭션을 보내지 않고, 이런 경우 주변 노드들에게 전파되지 않으므로 네트워크에 트랜잭션이 공유되지 않게 됩니다.

0개의 댓글