‘Mastering Bitcoin 2nd’ 세미나 8, 6장. Transactions 2/2
이번 세미나에서는 6장 Transactions의 Transaction Scripts and Script Language부터 진행합니다.
Transaction Scripts and Script Language
The bitcoin transaction script language, called Script, is a Forth-like reverse-polish notation stack-based execution language.
Both the locking script placed on an UTXO and the unlocking script are written in this scripting language. When a transaction is validated, the unlocking script in each input is executed alongside the corresponding locking script to see if it satisfies the spending condition.
비트코인 시스템은 잠금 스크립트에 대한 해제 스크립트를 결합하고 실행함으로 소유권 증명을 합니다. 잠금 스크립트와 해제 스크립트를 결합해서 실행했을 때 결과가 참이되면 소유권 있음으로 판단하고 트랜잭션을 정상적으로 처리합니다. 비트코인 시스템은 스크립트 작성을 위해 스택 기반 실행 언어인 Script를 사용합니다.
Script is a very simple language. For its use in validating programmable money, this is a deliberate security feature.
Script는 다른 프로그래밍 언어들에 비해 매우 간결합니다. 비트코인 시스템은 의도적으로 이러한 간결함을 추구합니다. 비트코인 시스템은 디지털 화폐를 다루기 위한 것(프로그래밍 가능한 돈을 다루기 위한 것)으로 그 목적을 국한하고 있습니다.
개발자 입장에서는 좀 더 다양한 프로그래밍 요소들을 지원해 주었으면 하고 바랄 수도 있지만, 프로그래밍 과정 중에 버그가 발생할 수 있고 돈을 다루는 프로그래밍에 있어 이러한 버그가 얼마나 심각한 문제가 될 수 있는지를 생각한다면, 비트코인 생태계가 추구하는 프로그래밍 언어의 간결함에 대해서 이해할 수 있을 것입니다.
The bitcoin transaction script language contains many operators, but is deliberately limited in one important way—there are no loops or complex flow control capabilities other than conditional flow control. This ensures that the language is not Turing Complete, meaning that scripts have limited complexity and predictable execution times. Script is not a general-purpose language.
Script를 다른 프로그래밍 언어와 비교할 때 가장 큰 차이는 튜링 완전하지 않다는 것입니다. 반복문이나 복잡한 흐름을 만들어 낼 수 있는 제어문들을 의도적으로 지원하지 않음으로 실행 경로를 분명하게 예측할 수 있도록 합니다.
The bitcoin transaction script language is stateless, in that there is no state prior to execution of the script, or state saved after execution of the script. Therefore, all the information needed to execute a script is contained within the script.
트랜잭션 스크립트는 stateless로 실행되기 때문에 스크립트 실행을 위해 필요한 모든 정보를 스크립트 내에 포함하고 있어야 합니다.
A script will predictably execute the same way on any system. If your system verifies a script, you can be sure that every other system in the bitcoin network will also verify the script, meaning that a valid transaction is valid for everyone and everyone knows this. This predictability of outcomes is an essential benefit of the bitcoin system.
비트코인 시스템은 트랜잭션 검증을 위해 UTXO set에 의존하기 때문에, 모든 노드가 동일한 트랜잭션에 대해 동일한 결과를 얻으려면 동일한 UTXO set을 가지고 있음을 전제로 해야 합니다. 비트코인 시스템은 UTXO set으로 현재 상태를 표현합니다.
본문 내용처럼 stateless하다는 것과 그에 따른 장점을 연결하려면, ‘이미 블록에 포함되어 있어 확정된 트랜잭션들(이력)만을 가지고 stateless이기 때문에 모든 노드들에서 동일한 결과를 얻을 수 있다’라고 하는 게 더 맞는 표현입니다. 현재가 아니라 과거에 촛점을 맞추고, 동기화에 있어서의 장점으로 연결한다면 설득력이 있을 것 같습니다.
Script Construction (Lock + Unlock)
비트코인 소유권 이동은 트랜잭션 입력과 출력으로 작성합니다. 트랜잭션 실행으로 소유권을 이동하려면 입력에 대한 잠금을 해제하고, 출력을 잠궈야 합니다.
비트코인 시스템 초기에는 잠금 스크립트를 작성하는데 공개키를 사용했기 때문에 잠금 스크립트 이름을 scriptPubKey라고 했습니다. 지금의 비트코인 시스템은 다양한 방법으로 잠금 스크립트를 작성하고 있지만, 이러한 이유로 그 이름은 여전히 scriptPubKey를 사용하고 있음에 주의합니다.
비트코인 시스템은 잠금 스크립트를 해제하는데(비트코인 소유권 이동을 위해) 디지털 서명을 사용하고 있기 때문에 잠금 스크립트 이름으로 scriptSig를 사용했습니다. Script를 사용해서 비트코인 소유권 이동이 아닌 경우에도 잠금 스크립트와 해제 스크립트를 작성할 수 있고, 이 경우 해제 스크립트에 디지털 서명을 사용하지 않을 수 있습니다. 그래도 이름은 scriptSig라는 것.
Each input contains an unlocking script and refers to a previously existing UTXO. The validation software will copy the unlocking script, retrieve the UTXO referenced by the input, and copy the locking script from that UTXO.
입력을 사용할 수 있는가는 입력이 UTXO set에 있는가에 의해 결정됩니다. 입력이 UTXO set에 있는 경우 입력이 참조하는 UTXO를 구하고, UTXO의 잠금 스크립트를 검증에 사용합니다.
The script execution stack
잠금 스크립트와 해제 스크립트는 그림 3과 같이 결합됩니다. 결합된 스크립트는 그림 4와 같이 스택 기반으로 실행됩니다.
초기 비트코인 클라이언트는 결합한 스크립트를 순차적으로 실행했습니다. 하지만 보안 문제로 인해 지금은 해제 스크립트를 먼저 실행하고 스크립트에 문제가 없으면 메인 스택으로 복사하고 잠금 스크립트를 실행합니다.
Pay-to-Public-Key-Hash (P2PKH)
잠금 스크립트는 누구에게 소유권이 있는지를 나타내는 것으로 ‘누구에게 지불할 것이냐’로 달리 표현할 수 있습니다.
비트코인 시스템에서 트랜잭션 스크립트 유형은 ‘누구에게 지불할 것이냐’에서 ‘누구에게’를 어떻게 정할 것인가에 따라 구분됩니다. 공개키로 누구에게를 결정한다면 ‘Pay to Public Key’가 되고, 공개키 해시값으로 누구에게를 결정한다면 ‘Pay to Public Key Hash’가 됩니다. 이 외에도 스크립트 해시값으로 누구에게를 결정하는 Pay to Script Hash도 있고 세그윗이 등장함에 따라 세그윗 방식으로 누구에게를 결정하는 다양한 방법 또한 있습니다. 이에 대해서는 7장에서 다룹니다.
잠금 스크립트와 해제 스크립트는 지불 유형에 따라 미리 결정된 형식을 사용합니다. 지갑 소프트웨어 개발자는 어떤 지불 유형을 사용할 것인가에 따라 정해진 형식에 따라 스크립트가 작성되도록 해야 합니다.
P2PKH는 공개키 해시값(hash160)으로 잠금 스크립트를 작성하는 방법을 사용합니다.
- 잠금 스크립트는 다음과 같은 형식으로 작성됩니다.
- OP_DUP OP_HASH160 <Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG
- 해제 스크립트는 다음과 같은 형식으로 작성됩니다.
- <Signature> <Public Key>
Digital Signatures (ECDSA)
The digital signature algorithm used in bitcoin is the Elliptic Curve Digital Signature Algorithm, or ECDSA. ECDSA is the algorithm used for digital signatures based on elliptic curve private/public key pairs.
비트코인 시스템은 타원 곡선 디지털 서명 알고리즘을 사용해서 디지털 서명을 생성하고 검증합니다.
본문 내용만으로 ECDSA를 제대로 이해하기는 어렵습니다. 좀 더 자세한 내용을 원하시는 분들은 Programming Bitcoin 책을 읽어 보시길 추천합니다. 여기서는 본문 내용이 원하는 수준으로 이해하고 넘어가도록 합니다.
ECDSA is used by the script functions OP_CHECKSIG, OP_CHECKSIGVERIFY, OP_CHECKMULTISIG, and OP_CHECKMULTISIGVERIFY. Any time you see those in a locking script, the unlocking script must contain an ECDSA signature. Any time you see those in a locking script, the unlocking script must contain an ECDSA signature.
비트코인에서 서명을 검증하는 다수의 연산들이 존재하는데 이러한 연산자가 실행되어야 할 때 마다 타원곡선 디지털 서명 알고리즘이 실행됩니다.
Creating a digital signature
디지털 서명은 다음과 같이 생성되고, DER(Distinguished Encoding Rules)로 직렬화되어 네트워크로 전송됩니다.
- Sig = Fsig (Fhash(m), dA)
- Sig is the resulting signature
- R과 S로 이루어짐
- dA is the signing private key
- m is the transaction (or parts of it)
- 트랜잭션에서 어떤 것을 메시지로 포함할 것인지는 서명해시유형(Signature Hash Types, SIGHASH)에 따라 달라집니다.
- Fhash is the hashing function
- Fsig is the signing algorithm
- S = k-1 (Hash(m) + dA * R) mod n
- k is the ephemeral private key
- 개인 키를 직접 사용할 때의 보안 위험을 피하기 위해서 사용하는 임시 개인 키
- R is the x coordinate of the ephemeral public key
- k * G(생성원)으로 구한 임시 공개 키의 x 좌표
- dA is the signing private key
- m is the transaction data
- n is the prime order of the elliptic curve
- k is the ephemeral private key
- S = k-1 (Hash(m) + dA * R) mod n
- Sig is the resulting signature
Verifying the Signature
서명 검증은 다음과 같이 임시 공개키를 구하고, 임시 공개키의 x 값이 서명의 r 값과 같은지를 비교합니다.
- P = s-1 * Hash(m) * G + s-1 * R * <공개키>
- R and S are the signature values
- 해제 스크립트의 서명으로 전달 됨
- m is the transaction data that was signed
- 서명 유형에 따라 트랜잭션 데이터로 결정
- G is the elliptic curve generator point
- secp256k1에 상수 값으로 정해져 있음
- R and S are the signature values
Signature Hash Types (SIGHASH)
Digital signatures are applied to messages, which in the case of bitcoin, are the transactions themselves.
디지털 서명은 메시지에 대해 합니다. 비트코인 시스템에서 메시지는 트랜잭션에 해당합니다. 보통은 트랜잭션 전체 데이터에 대해서 행해집니다. 하지만 트랜잭션에서 어떤 데이터를 메시지로 사용할 것인지를 정할 수 있도록 하면 좀 더 다양한 조건을 만족하는 사용사례를 만들어 낼 수 있습니다. 이를 위해 등장한 것이 SIGHASH입니다.
비트코인 서명 끝에 1바이트 크기로 SIGHASH가 작성됩니다.
트랜잭션은 입력과 출력으로 구성됩니다. 입력과 출력은 여러 개 있을 수 있습니다. 트랜잭션에서 서명이 필요한 부분은 입력 부분입니다. 한 사용자는 다수의 키쌍을 가질 수 있기 때문에 서로 다른 키로 서명한 입력들을 하나의 트랜잭션에서 사용해야 할 수 있습니다. 이러한 이유로 서명은 입력 별로 작성되어야 합니다.
SIGHASH는 입력을 포함할 것인지는 설정하지 않습니다. 모든 입력은 메시지에 포함합니다. SIGHASH가 0x01(ALL)이면 출력도 모두 포함하고, 0x02(NONE)이면 출력을 포함하지 않고, 0x03(SINGLE)이면 입력과 같은 인덱스 값을 갖는 출력만 포함하도록 합니다.
SIGHASH에는 ANYONECANPAY(0x80)라는 유형이 있는데 위에서 설정한 유형들과 조합(bitwise or)해서 사용할 수 있습니다.
ANYONECANPAY는 입력 자신만 메시지 데이터로 포함하도록 합니다.
어떤 데이터를 메시지로 포함할 것인지 그렇지 않을 것인지를 정한다는 것은 어떤 부분이 변경될 수 있는지를 정하는 것입니다. 서명 대상이 되는 메시지에 포함되면 그 데이터는 변경할 수 없는 것이 됩니다. 만약 출력을 메시지 데이터로 포함하지 않는다면 출력은 서명 대상이 아니기 때문에 출력 내용은 자유롭게 변경이 가능합니다.
Bitcoin Addresses, Balances, and Other Abstractions
블록체인 탐색기만 접하고 비트코인 시스템을 잘 모르는 경우 블록체인 탐색기에서 보여주는 것과 같이 비트코인 시스템이 되어 있을거라고 오해하는 개발자들이 있습니다. 블록체인 탐색기는 사용자 친화적으로 정보를 제공하기 위해 많은 일을 추가적으로 하고 있습니다.
그림 8의 왼쪽 부분에서 앨리스의 비트코인 주소를 볼 수 있습니다. 사실 트랜잭션에는 이런 정보가 없습니다. 블록체인 탐색기는 트랜잭션의 입력에서 입력이 참조하는 UTXO를 구할 수 있습니다. 블록체인 탐색기는 출력의 잠금 스크립트에서 공개키 해시값을 구하고, 이것을 Base58Check 인코딩해서 주소로 보여줍니다.