비트코인 스크립트

비트코인 스크립트
⚠️
해당 문서는 일부 비트코인 백서에 대한 이해를 바탕으로 하고 있습니다. 아직 백서를 읽지 않았다면, 빠르게 읽어보시는 것을 추천합니다.

블록체인과 프로그래밍

비트코인을 제외한 유명한 체인은 스마트 컨트랙트(smart contract) 기반으로 많은 것을 할 수 있습니다. 스마트 컨트랙트를 단순하게 설명하면 "특정 방식으로만 동작하는 코드"입니다. 블록체인이 iOS나 윈도우라면, 스마트 컨트랙트는 개발자들이 원하는 프로그램을 만들 수 있습니다. 예를 들어 ERC-721로 대표되는 NFT(Non Fungible Token)은 예시로 다음과 같은 동작이 있습니다.

  • ownerOf: 특정 넘버링 토큰(예. 이미지)마다 소유자를 매칭시켜둔 데이터베이스
  • transferFrom: 특정 넘버링 토큰은 소유자 A에서 소유자 B로 전달

이제 두 동작을 통해 서로 이미지를 주고 받을 수 있고, 다른 행위를 못한다면? 그리고 표준을 잘 따랐다면 이런 전송 기능은 오롯이 본인의 선택에 달려 있기 때문에 우리는 이 디지털 데이터베이스에서 각자의 소유라고 여길 수 있게 되는 것입니다. 물론 표준은 아니더라도 원하는 동작이 있으면 다양한 프로그래밍을 할 수 있으며 그렇기에 표준이 아닌 경우에는 종종 해킹, 공격 등이 이뤄지기도 합니다. 그래서 표준에 대한 논의는 언제나 많은 생태계 참여자의 토론을 불러일으킵니다.

이더리움을 포함한 이더리움과 유사한 환경에서는 솔리디티(Solidity)라는 언어를 사용합니다. 이는 일반적으로 개발에 사용되는 파이썬(Python)이나 자바스크립트(JavaScript)와 유사한 문법을 가지고 있으며 원하는 대부분의 프로그래밍을 할 수 있습니다.

하지만 비트코인은 처음에 나올 때는 이런 프로그래밍이 없었습니다. 이미 내부에서 작성된 서로의 전송과 서명 매커니즘으로만 구성된 디지털 화폐였지, 어떠한 동작을 목표로 하고 있지는 않았습니다. 하지만 2010년부터 거래 조건을 명시하고 검증하는 간단한 프로그래밍 언어의 형태가 제안되기 시작했습니다.

👀
비트코인 스크립트 자체의 창시자에 대해서는 명확한 자료는 없습니다. 대략적으로 사토시가 활동하던 시기라 사토시도 이를 만든 주축 중 하나이지 않을까 예측해볼 수 있습니다.

비트코인 스크립트

비트코인 스크립트의 특징

그래서 지금은 제한적이지만 비트코인에도 프로그래밍을 할 수 있습니다. 비록 솔리디티만큼의 동작은 못합니다. 비트코인 스크립트의 특징은 다음과 같습니다.

  1. 다른 스마트 컨트랙트 언어와 "튜링 불완전성"입니다. 대표적으로 비트코인에는 "특정 로직을 반복"하는 반복문 기능이 제외되어 있습니다. 반복문을 통해 무한반복이 이뤄질 경우 전체 시스템의 보안 문제에 영향이 갈 수 있기에 의도적인 제외이기도 합니다.
  2. 반복 외에도 문자열 조작 등 연산이 다른 언어에 비해 제한되어 있습니다.
  3. "상태를 저장"하지 않습니다. 다른 스마트 컨트랙트 언어는 공동된 데이터베이스를 가지고 해당 상태를 계속해서 사용합니다. 즉 NFT를 서로 주고 받으면 그 데이터가 블록체인 위에서 모두 기록되지만, 비트코인은 스크립트마다 독립적인 제한된 기능만 하게 됩니다.
  4. (* 개발자를 위한 첨언 ) 스택 기반의 언어입니다. only stack 입니다. 물론 다행이 선입후출(First In Last Out, FILO)만 사용하는 것은 아니고 특정 위치의 값을 복사하여 스택의 상단에 Push할 수 있습니다.

스크립트 언어의 구조

어셈블리를 좋아하는 분들이라면 익숙한 구조입니다. 스크립트 언어는 특정 연산을 제공하는 최소 단위인 연산자(Opcodes, OP)와 연산에 사용하는 일부 데이터(수)로 작성합니다. 그리고 왼쪽부터 오른쪽으로 한 연산이 동작됩니다. 스택 구조와 OP Code는 하나의 접시 쌓기라고 생각하면 됩니다. 예를 들어 1+2를 구하려면 다음과 같습니다.

  1. 접시를 다음과 같이 표현하겠습니다. []
  2. 1이라는 데이터를 넣습니다. [1]
  3. 2라는 데이터를 넣습니다. [1, 2]
  4. 상단 접시의 두 개를 빼서 합한 뒤 다시 넣습니다. [3]

이를 스크립트 언어로 표기한다면 OP_1 OP_2 OP_ADD 라고 표기할 수 있습니다. (왜냐하면 1과 2는 OP로 각각 1과 2를 넣는 작업을 의미합니다. 16까지 가능합니다)

잠금 스크립트와 해제 스크립트

스크립트는 잠금 스크립트(locking script)와 해제 스크립트(unlocking script)로 나뉩니다. 쉽게는 잠금 스크립트가 함수, 해제 스크립트가 변수라고 생각하시면 됩니다. 잠금 스크립트는 UTXO에 포함되며 이를 동작시킬 해제 스크립트를 기다리고 있습니다. 해제 스크립트는 일반적으로 <디지털 서명>, <공개 키> 등이 포함됩니다.

다음은 P2PKH(Pay-to-Public-Key-Hash)라는 특정 사용자만 사용할 수 있는 비트코인을 보내는 스크립트의 잠금 스크립트의 예시입니다.

OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
  1. OP_DUP: 스택의 맨 위 요소를 복제하여 스택에 한 번 더 추가
  2. OP_HASH160: 스택 맨 위의 요소에 대해 SHA-256 해시 계산
  3. OP_EQUALVERIFY: 스택 맨 위의 두 요소를 비교하여 같은지 확인. 같으면 스택에서 제거하고 계속 진행. 다르면 스크립트 종료
  4. OP_CHECKSIG: 스택의 두 요소를 사용하여 서명 검증. 첫 번째 요소에 서명, 두 번째 요소에 공개키가 있을 때, 이 두 값을 통해 검증. 검증 결과가 참이면 1, 아니면 0 추가

참고로 여기서 해제 스크립트의 입력은 <서명> <pubKey>입니다.

  1. 처음에는 <서명>과 <pubKey>가 스택에 들어가게 됩니다. [서명, pubKey]
  2. pubKey를 복사합니다. [서명, pubKey, pubkey]
  3. 맨 상단의 값을 해시함수로 계산합니다. [서명, pubKey, pubKeyHash]
  4. 상단에 <pubKeyHash>를 추가합니다. [서명, pubKey, pubKeyHash, pubKeyHash]
  5. 그리고 상단의 두 값이 같다면 스택에서 제거하고, 다르다면 종료합니다. 여기서는 같다고 가정한 상황이니 제거됩니다. [서명, pubKey]
  6. 그렇게 되면 남은 두 값으로 유효성 검증을 합니다. 기존의 <서명>과 <pubKey>를 통해 검증이 성공합니다.

"아니 어짜피 서명이랑 공개키있으면 그냥 되는거 아니야?"라고 할 수 있습니다. 다만 이 방식을 사용하게 되면 <공개키>를 온체인 상에서 공개하지 않고 (공개키 해시값이므로) 공개키에 해당하는 사람이 유효한 UTXO를 사용할 수 있도록 하는 정보 보호가 가능합니다.

💡
다중 서명(Multi-Signature)도 매우 쉬운데 이는 각자 찾아보는 것을 추천합니다. 저는 살펴보며 해시 충돌이 발견되면 인센티브를 주는 스크립트가 상당히 재밌었는데 궁금한 분들은 살펴보시기 바랍니다.

무한으로 코드 작성이 가능한가?

아니요 기본적으로 합의된 스크립트의 제한은 10,000 바이트 입니다. 이는 버전마다 변경되었으며 확장성을 위해 하드포크했던 비트코인 캐시의 경우는 이 제한을 없애버렸습니다. 단 비트코인 캐시 또한 블록 사이즈에는 제한이 있기에 제한이 없다고는 할 수 없습니다.

또한 P2SHP2WSH, P2TR 등 다양한 로직의 상황에서는 각각 제한이 별도로 존재합니다. 해당 내용이 궁금한 분은 이 링크를 통해 확인하시면 됩니다.

현재 연산자는 어떤 게 있나요?

연산자는 지속적으로 논의를 통해 추가되고 빠지고 있습니다. 구체적인 연산자를 확인하고 싶다면 다음 링크를 참고하세요. 현재 약 100 개정도 존재합니다.

Script - Bitcoin Wiki

다음 편은?

최근의 비트코인 내러티브를 이해하기 위해서는 다음과 같은 내용에 대해 이해가 필요합니다.

  1. 세그윗(SegWit) & 탭루트(Taproot) 업그레이드: 오디널스와 BRC-20
  2. OP_RETURN: 룬 프로토콜(Rune Protocol)
  3. OP_CAT: CatVM

차차 프로그래밍 구현 측면에서 글을 작성해보겠습니다. 😄