비트코인 스크립트

비트코인 스크립트

비트코인 스크립트란?

비트코인에서 스크립트(script)는 스택 기반의 아주 단순한 프로그래밍 언어이다. 비트코인은 스크립트로 작성된 코드를 읽어 이를 실행하면서 코인 트랜잭션을 수행한다. 비트코인 스크립트 언어는 의도적으로 루프(loop)를 허용하지 않았으며, 이는 인터프리터가 스크립트를 실행할 때, 무한루프에 빠져 크래시하는 것을 원천적으로 방지할 뿐만 아니라, 트랜잭션을 불필요하게 복잡하게 구현하지 못하도록 하는 장점을 제공한다. 비트코인 블럭체인은 루프를 허용하지 않는 Turing Incomplete 스크립트를 사용하는 반면, 이더리움과 같은 몇몇 블럭체인들은 루프를 허용하면서 Turing complete 스크립트를 지원하고 있다.

비트코인의 트랜잭션 스크립트는 프로그래밍 언어이므로 트랜잭션에 다양한 스크립트를 넣을 수 있게 되어 있지만, 일반적으로 사용되는 표준 스크립트(standard script)로는 P2PKH (pay to pubkey hash), P2SH (pay to script hash), P2PK (pay to pubkey), P2MS (pay to multisig) 등이 있다. 현재 가장 많이 사용되는 것은 P2PKH와 P2SH 스크립트이다.

트랜잭션의 스크립트

비트코인의 트랜잭션은 "비트코인 스크립트"를 실행하여 트랜잭션이 타당한 지를 검증한다. 트랜잭션의 검증은 이전 블럭의 사용되지 않은 트랜잭션 출력(transaction output, UTXO)과 이를 사용하기 위해 작성하는 트랜잭션 입력(transaction input)을 합쳐 실행 가능한 비트코인 스크립트를 만들고, 이를 실행해서 TRUE가 리턴되면 트랜잭션이 타당하다고 판단한다. 트랜잭션 검증을 위한 스크립트는 먼저 트랜잭션 입력 스크립트(ScriptSig)를 앞에 두고, 뒤에 트랜잭션 출력 스크립트(ScriptPubKey)를 합쳐 하나의 스크립트로 만든다. 아래 그림은 이러한 트랜잭션 스크립트 구성을 표현한 것이다.

비트코인 스크립트 언어

비트코인은 트랜잭션 처리를 위해 스크립트 언어를 사용하는데, 이 스크립트(Script) 언어는 처음에 비트코인을 위해서만 만들어 졌다. 스크립트는 특별하게 이름이 붙여지지 않아서, 일반적으로 비트코인 스크립트 언어 혹은 단순히 Script라고 불리운다. 스크립트(Script)는 1970년 대에 개발된 Forth 프로그래밍 언어에서 영감을 받아 만들어졌는데, 스택 기반의 간단한 절차적 프로그래밍 언어이다. 스크립트는 제한된 CPU와 메모리 상에서 사용될 수 있으며, 특히 루프(loop)를 지원하지 않는다.

비트코인 스크립트 명령어(OP Codes)에는 모두 256개의 OP 코드가 있는데, 이 중 15개는 Disable 되었으며, 75개는 예약어(reserved)이다. OP 코드에는 기본적인 상수(constants) 산술 명령어(arithmetic)를 비롯하여 조건문 (flow control), 스택 처리(stack operation), 비교/논리 로직(bitwise logic), 암호관련 명령어(hash, signature check) 등이 있다.

카테고리 명령어 OP 코드 설명
상수 OP_0, OP_FALSE 0x00 제로(0)가 스택에 Push됨
OP_1, OP_TRUE 0x51 (81) 1이 스택에 Push됨
OP_2 ~ OP_16 0x52 ~ 0x60 (82~96) 숫자(2 ~ 16)가 스택에 Push됨
제어 OP_IF 0x63 (99) 스택 Top이 FALSE가 아니면, 실행함 (Top은 제거)
OP_ELSE 0x67 (103) 이전의 IF문이 실행되지 않았으면, ELSE 가 실행됨
OP_ENDIF 0x68 (104) IF을 종료함. 모든 IF 조건문은 ENDIF로 종료되어야 함
OP_VERIFY 0x69 (105) 스택 Top이 TRUE가 아니면, 트랜잭션을 Invalid로 마크함
스택 OP_DUP 0x76 (118) 스택 Top을 복제한 후, 스택에 넣음
비교/논리 OP_EQUAL 0x87 (135) 두개의 입력이 일치하면 1, 아니면 0을 리턴
OP_EQUALVERIFY 0x88 (136) OP_EQUAL + OP_VERIFY. 두개의 값이 일치하지 않으면 트랜잭션을 에러로 처리
산술 OP_ADD 0x93 (147) 덧셈
OP_SUB 0x94 (148) 뺄셈
암호/해시 OP_RIPEMD160 0xa6 (166) 입력값을 RipeMD-160 으로 해싱함
OP_SHA256 0xa8 (168) 입력값을 SHA256 으로 해싱함
OP_HASH160 0xa9 (169) 입력값을 SHA256 으로 해싱한 후, 다시 RipeMD-160 으로 해싱함
OP_HASH256 0xaa (170) 입력값을 SHA256 으로 두번 해싱함(double hashing)
OP_CHECKSIG 0xac (172) 서명(sig)과 public key(pubKey)를 입력받아 서명이 타당한지 검사하여 타당하면 1을 리턴함
OP_CHECKSIGVERIFY 0xad (173) OP_CHECKSIG + OP_VERIFY. 서명을 검증하고 만약 틀리면 트랜잭션을 에러로 표시.
OP_CHECKMULTISIG 0xae (174) 다중서명(MultiSig)을 체크하는 명령어로, 여러 서명 중 조건에 지정된 수만큼의 서명이 만족되면 서명을 타당하다고 판단한다.

스크립트 예

비트코인 스크립트에서 가장 많이 쓰이는 P2PKH 스크립트는 다음과 같은 포맷으로 되어 있다.

트랜잭션 출력 스크립트 (scriptPubKey):
    OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
    
트랜잭션 입력 스크립트 (scriptSig):
    {sig} {pubKey}

P2PKH 스크립트는 "트랜잭션 입력 스크립트" + "트랜잭션 출력 스크립트" 순으로 합쳐, 이 스크립트를 실행했을 때 트랜잭션이 타당해야 한다. ScriptSig는 디지탈 서명과 공개키(public key)를 가지고 있으며, ScriptPubKey는 공개키 해시를 사용하여 ScriptSig의 공개키를 체크한 후 ScriptSig의 디지탈 서명이 맞는지를 체크하게 된다.

    {sig} {pubKey} OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG

위의 P2PKH 스크립트는 다음과 같은 순서로 실행된다.

  1. {sig}: 디지탈 서명을 스택에 넣는다
  2. {pubKey}: 공개키(public key)를 스택에 넣는다
  3. OP_DUP: 스택 Top에 있는 공개키(pubKey)를 복제하여 복사한 키를 다시 스택에 넣는다
  4. OP_HASH160: 스택에서 (복제하여 추가한) 공개키를 꺼내 SHA256으로 해싱한 후, 다시 RIPEMD-160으로 해싱하여 스택에 넣는다. 즉, OP_HASH160은 RIPEMD160(SHA256(pubKey)) 값을 계산한다.
  5. {pubKeyHash}: (트랜잭션 출력에 있었던) 공개키 해싱값(pubKeyHash)을 스택에 넣는다. 이는 개념적으로 비트코인 주소(bitcoin address)와 같은 것인데, 왜냐하면 이 해시값으로부터 비트코인 주소가 도출되기 때문이다.
  6. OP_EQUALVERIFY: 스택에서 2개의 아이템(4번과 5번 아이템)을 꺼내 둘을 비교하고 같으면 다음 명령어로 이동하고, 틀리면 처리를 멈추고 에러를 발생한다. 이 비교 과정은 개념적으로 트랜잭션 입력의 ScriptSig가 제공한 비트코인 주소(주: 비트코인 주소는 공개키로부터 도출됨)가 트랜잭션 출력의 ScriptPubKey에 있는 비트코인 주소(주: 공개키 해시에서 도출되는 비트코인 주소)와 동일한 지를 체크하는 것이다. 이 과정으로 자신이 가진 비트코인 주소의 코인을 사용하는 것인지 체크하게 된다.
  7. OP_CHECKSIG: 스택에 남은 2개의 아이템(1번과 2번 아이템)을 꺼내 ECDSA 디지탈 서명이 맞는지 체크한다. 맞으면 스택에 1 (TRUE)을 넣고, 틀리면 0 (FALSE)을 넣는다. 스택에 1 (TRUE)이 남으면, 스크립트가 성공한 것으로 간주한다. 디지탈 서명은 해당 공개키(public key)와 매핑되는 개인키(private key)를 가져야만 생성할 수 있으므로, 만약 서명이 맞는다면, 그 사람이 개인키를 소유하고 있다는 것을 증명하는 것이다.