블록체인 5 - Scripts (1)

트랜잭션에 사용되는 언어, Script 에 대해 알아보자.

트랜잭션에는, 컴퓨터가 자동으로 검사할 수 있는 작은 프로그램(코드) 가 포함되어 거래가 이루어지는데, 이 코드를 스크립트라고 부른다.


  • 트랜잭션의 아웃풋
  • 트랜잭션의 output이란, 누군가에게 비트코인을 보낼 때 만들어지는 '돈의 도착지 정보'다.
  • 구성요소
    • 값 : 0.015
    • scriptPubKey(누가 쓸 수 있는가) : 잠금 스크립트로, 이 스크립트를 풀 수 있는 개인키를 가진 사람만이 이 돈을 꺼내 쓸 수 있다.

  • 트랜잭션의 인풋
  • 트랜잭션 input이란 : 돈을 쓸 때, 내 지갑의 어디에서 돈을 가져오는지 증명하는 부분이다.
  • 구성요소
    • 어느 돈을 쓸래?
      • txid : 이전에 받은 돈이 기록된 트랜잭션 id
      • vout : 해당 트랜잭션에서 몇번째 출력인지 가리키는 인덱스
    • 주인인걸 증명해
      • scriptSig : UTXO에 걸린 조건을 만족시켜 잠금을 해제하는 데이터(서명 등) -> output의 자물쇠를 여는 서명과 공개키다.

트랜잭션의 인풋이랑 아웃풋이 각각,

  • 아웃풋은 비트코인의 값과 자물쇠, 즉 스크립트펍키로 돼있고,
  • 인풋은 전 거래의 id와 인덱스, 그리고 스크립트펍키를 열수있는 개인키의 공개키와 그 서명으로 돼있는 스크립트시그로 돼있다고 이해했다.

근데, 인풋에는 얼마가 들어와있는지 적혀있지 않다.

  • 그런데 채굴자(노드)는 수수료를 계산해야한다.
  • 수수료 = (입력으로 들어온 돈 총액) - (출력으로 나가는 돈 총액)
  • 나가는 돈은 이번 트랜잭션에 적혀있지만 들어온 돈은 알 수가 없다.
  • 그래서, 옛날 트랜잭션의 나가는 돈을 봐야하는거다.

다시 복습하면,

  • Authorization (권한 부여) = scriptPubKey (자물쇠)
    • 아웃풋에 있는 scriptPubkey
    • 돈을 보낼 때 발생하고, 이 돈은 철수의 공개키를 가진 사람만 쓸 수있다. 라는 식의 정책을 정해서 자물쇠를 거는 행위임
  • Authentication(인증) = scriptSig(열쇠) - 인풋에 적혀있음
    • 돈을 쓸 때 발생하고, 철수가 와서, "내가 철수 맞다. 여기 내 서명봐라: 고 증명하는 과정임.

스크립트 언어의 세가지 특징

  • 단순한 스택기반
  • Stateless : 스크립트 실행 전이나 후에 저장되는 상태(변수 등)이 없다.
  • 튜링 불완정성 : 루프나 복잡한 흐름제어가 없다.

  • 검증 엔진은, 트랜잭션을 검증하기 위해 input 스크립트와 output 스크립트를 사용한다.
  • 입력 스크립트를 복사하고, 참조된 utxo의 출력 스크립트를 가져와서 이어붙여 실행한다.
  • 출력 스크립트의 조건을 정확히 만족시켜 결과가 나오면 소비됨 (spent)로 간주된다.
  • 어떤 조합이든 결과가 True가 나오면 유효하다.
  • P2PKH ( Pay to Public Key Hash) : 가장 흔한 레거시 비트코인 거래방식

P2PKH ( Pay to Public Key Hash) : 가장 흔한 레거시 비트코인 거래방식

  • 해제 스크립트와 잠금 스크립트가 결합된 모습
  • 왼쪽 : <sig> <PubK> : 사용자가 제출한 서명과 공개키
  • 오른쪽 : DUP HASH160 <PubKHash> EQUALVERIFY CHECKSIG : 블록체인에 저장돼있던 자물쇠 - 복사, 해시, 비교, 서명확인의 조건.

  • 스택 연산의 작동 방식 설명
  • 2 3 ADD 5 EQUAL 이면,
  • 2랑 3을 스택에 차곡 차곡 쌓고 (push)
  • ADD로 맨위에 두개 숫자를 꺼내서 더하고 결과를 다시 넣는다. (pop & push)
  • 비교할 정답숫자 5를 스택에 넣는다
  • EQUAL : 맨위에 두개 꺼내서 같은지 보고, 같으면 TRUE를 넣는다.

  • 스택 연산자가 이런식으로 작동하는겨

  • P2PKH 실행과정 (준비단계)
  • 사용자가 입력한 <서명><공개키>를 스택에 넣고, OP_DUP 명령어를 실행해서 맨위에 있는걸 복사한다.
  • OP_HASH160: 맨 위 데이터를 해싱한다.
  • OP_EQUALVERIFY: 스택의 해시값과 잠금 스크립트(Output)에 있던 해시값이 같은지 비교하고 제거한다.
  • OP_CHECKSIG: 남은 공개키와 서명을 대조하여 유효하면 TRUE를 반환한다.

고급 트랜잭션들

  • Multisignature (다중서명) : 여러명 도의 필요
  • Pay-to-Script-Hash : 나중에 배움여
  • Timelocks : 시간제한 걸기

위에서 도표로 본건 기본 예금이고, 계모임 통장같은 개념이나 특수 기능도 알아보자.


Multisignature

  • M of N 구조 : N명의 관리자 중 M 명 이상이 서명해야 출금 가능.
  • 스크립트 형태 :
M <키1> <키2> ... <키N> N OP_CHECKMULTISIG

예시: 2-of-3 (3명 중 2명 서명 필요).

버그 : CHECKMULTISIG 명령어 구현상의 실수로, 실행 시 스택에서 의도한 것보다 아이템을 하나 더 꺼내는(Pop) 버그가 있다.

해결책 : 입력 스크립트(서명) 맨 앞에 0 또는 OP_0 이라는 의미없는 더미값을 추가해줘야한다.


복잡한 Multisig의 문제점

ex) 모하메드가 5명중 2명의 서명이 있어야 돈을 쓸 수 있는 2 of 5 계좌로 돈을 보내려고 한다.

문제점들

  1. 복잡성 : 모하메드는 돈을 보내기 전에 파트너 5명의 공개키 목록이 담긴 전체 스크립트 내용을 미리 알아야한다.
  2. 용량과 수수료 : 공개키가 5개나 들어가서 트랜잭션의 크기가 일반 거래보다 약 5배 커진다. 그러면 수수료가 비싸진다.
  3. 네트워크 부담 : 이 거대한 스크립트는 누군가 사용할 때까지 계속 모든 비트코인 노드의 UTXO 데이터베이스에 저장되어있어야 해서 네트워크에 부담을 준다.

해결책 : P2SH (Pay-to-Script-Hash)의 등장

  • 핵심 아이디어 : 복잡하고 긴 스크립트 전체를 트랜잭션에 넣는 대신, 스크립트의 해시값만 넣는다.
  • 작동 방식
    • 돈을 보낼 때 짧은 해시값으로만 잠가둔다
    • 나중에 돈을 사용할 때, 원래의 긴 (Redeem Script)를 제출해서 해시값이 맞는지 증명한다.
  • 결론 : P2SH는, 이 해시값과 일치하는 스크립트를 나중에 가져오면 돈을 주겠다. 는 뜻이다.
  • 1. p2sh 적용 전에는, 잠금 스크립트에 모든 공개키가 다들어가 엄청 길어진다.
  • 2. p2sh 적용 후에는, 긴 공개키 목록을 별도로 빼두고, 잠금스크립트에는 20바이트의 짧은 해시값만 넣어둔다.
  • 나중에 돈을 받는 사람이 돈을 쓸 때, 서명과 함께 원래의 긴 'Redeem Script'를 제출한다.

  • 핵심 변화 : 데이터 용량과 수수료 부담이 보내는 사람에서 받는 사람으로 이동했다. 보내는 사람은 짧은 주소로 보내면되고, 쓸 사람이 복잡한 내용을 처리한다.

  • p2pkh는 페이 투 퍼블릭 키 해시라는 뜻이다.
  • p2sh 라는건, 페이 투 스크립트 해시라는 뜻이다.
  • 이름에서 알 수 있듯이, p2sh는, 복잡한 조건이 담긴 스크립트의 해시에다가 지불한다는거니까, 리딤 스크립트(Code = "2 <PubA> <PubB> ... <PubE> 5 CHECKMULTISIG") 다시 해시해서 거기로 돈을 보낸다는거다.

  • 그래서 p2sh 로 받은 트랜잭션을 어떻게 쓰는가?
  1. 본인확인 : 금고를 열기 위해 가져온 설명서(스크립트)가 처음에 약속한 설명서가 맞는가? : 해시값 대조
  2. 내용 실행 : 설명서가 맞다면, 조건을 만족했는가? : 스크립트 실행

(1) 2 <Mohammed's Public Key> ... 5 CHECKMULTISIG 이건 뭘까?

  • 이것이 바로 리딤 스크립트(Redeem Script), 잠금해제 규칙이다.
    • 2: "최소 2명의 서명이 필요하다" (M).
    • <PK1> <PK2> ...: 서명을 할 수 있는 후보자들(모하메드, 파트너1, 변호사 등)의 공개키 목록입니다.
    • 5: "후보자는 총 5명이다" (N).
    • CHECKMULTISIG: "스택에 있는 서명들을 확인해서, 위 5명 중 2명의 서명이 맞는지 검사하라"는 명령어(함수)입니다.

(2) HASH160은 뭔가요?

  • 이것은 해시 함수 명령어
    • 기능: 스택의 맨 위에 있는 데이터를 가져와서 RIPEMD160(SHA256(데이터)) 연산을 수행합니다.
    • 결과: 긴 데이터를 20바이트짜리 고유한 '지문(Hash)'으로 바꿔줍니다.

(3) 54c557e0... (파란색 글씨)

위의 긴 리딤 스크립트(규칙)를 HASH160 함수에 넣어서 나온 결과값(해시값)이다. 블록체인에는 이 짧은 값만 기록된다.

2. 검증 과정 단계별 설명

Step 1: "가져온 규칙이 맞는가?" (무결성 검사)- 이미지의 (1)번 박스

코드: <2 PK1 ... CHECKMULTISIG> HASH160 <redeem scriptHash> EQUAL
  1. <2 PK1 ...>: 사용자가 돈을 쓰겠다고 리딤 스크립트 원본(Raw Code)을 제출합니다. 이 긴 코드가 스택에 올라감
  2. HASH160: 제출된 원본 코드를 해시 -> 계산된 해시값이 나옴
  3. <redeem scriptHash>: 블록체인에 원래 기록되어 있던(잠겨 있던) 해시값(54c55...)을 스택에 올림
  4. EQUAL: 계산된 해시값 == 기록된 해시값 인지 비교
    • 통과: "네가 가져온 코드가 처음에 약속한 그 코드가 맞구나." (다음 단계로)
    • 실패: "가짜 코드를 가져왔네?" (거래 거절)

Step 2: "규칙을 만족했는가?" (실행 검사)

Step 1이 통과되면, 이제 제출했던 리딤 스크립트의 주석 처리가 풀리고(Deserialize) 실제로 실행됨.

코드: <Sig1> <Sig2> 2 <PK1> ... <PK5> 5 CHECKMULTISIG
  1. <Sig1> <Sig2>: 사용자가 제출한 전자서명 2개가 스택에 쌓임
  2. 2 <PK1> ... <PK5> 5: 아까 검증된 리딤 스크립트의 내용들이 스택에 쭉 쌓임
  3. CHECKMULTISIG: 이 함수가 실행되면서 스택에 있는 데이터들을 싹 가져감.
    • "후보자 5명(PK1~PK5) 중에 서명 2개(Sig1, Sig2)가 유효한 게 맞는가?"
  4. 결과: 맞으면 TRUE, 틀리면 FALSE.

P2SH 의 특징과 주의점

  • p2sh 를 사용하면, 데이터용량, 수수료부담, 구현의 복잡성의 부담이 모두 받아서 사용하는 사람에게 이전된다.
  • p2sh 안에 또 p2sh를 넣는 건 안된다.
  • 또한, 실수로 무효한 스크립트의 해시값을 만들어 돈을 보냈다면, 그 돈은 영원히 못찾는다.

데이터 기록용 OUTPUT (OP_RETURN)

  • 비트코인을 화폐가 아닌 데이터 저장소로 사용할 수 있다.
  • 하지만 반대론자들이 내세우는 문제점
    • 블록체인 비만(Bloat) : 결제가 상관없는 데이터가 쌓이면 풀노드들의 저장공간을 낭비한다.
    • 가짜 UTXO 문제 :
      • 초기에는 데이터를 남기기 위해 가짜 비트코인 주소를 만들어서 돈을 보냈다.
      • 이 주소는 누구의 것도 아니기에 돈을 절대 사용할 수 없다.
      • 이는 평생 UTXO로 남아서 utxo set 에 평생 남아서 노드의 성능을 크게 떨어뜨린다.
  • 해결책 : OP_RETURN
    • 거래 출력에 80바이트까지 데이터를 적을 수 있다.
    • OP_RETURN은 명백히 못쓰는 돈이라고 선언하는 것이기에 이 데이터는 블록체인 디스크에는 기록되지만 노드의 메모리(ram)을 낭비하지는 않는다.