표준 스크립트
비트코인은 (SegWit 이전에) 다음과 같은 4개의 표준 스크립트(Standard Scripts)를 가지고 있었는데, 이 중 현재 사용되는 것은 P2PKH와 P2SH 스크립트이다.
위 그림에서 Sig는 디지탈 서명(Signature)를 가리키는 것으로, 서명을 DER 인코딩한 후 SIGHASH(1 byte)를 뒤에 추가한 것이다 (즉, Sig = DER Signature + SIGHASH). 또한, Sig 혹은 PubKey 앞에는 그 데이타의 길이가 추가되는데, 이는 그 데이타를 스택에 푸시하는 기능을 한다.
P2PK
P2PK (Pay To Pubkey)는 Public Key (raw bytes)에게 지불하는 스크립트로서, Locking Script는 [publicKey] + [OP_CHECKSIG] 로 되어 있으며, Unlocking Script는 [Signature] 로 되어 있다.
Locking Script는 Public Key의 사이즈만큼을 스택에 푸시하기 위해 Public Key의 길이를 맨 앞에 두게 되고, Unlocking Script도 Signature 길이만큼을 스택에 푸시하게 위해 Signature의 길이를 맨 앞에 두게 된다.
Unlocking Script와 Locking Script를 접목하면, [Signature] + [publicKey] + [OP_CHECKSIG] 이 되고, 이를 좀 더 자세히 표시하면 아래와 같이 된다.
Signature크기 + Signature + publicKey크기 + publicKey + OP_CHECKSIG(0xac)
위와 같이 크기를 앞에서 써서 스택에 푸시하게 하는 부분은 아래 표준 스크립트들에도 공히 적용된다.
P2PK는 Public Key를 노출하게 되어 보안상 덜 안전한 방식인데, 이를 좀 더 보완하기 위해 Locking Script에서 Public Key를 해싱한 Public Key Hash를 노출하게 하고, Unlocking Script에서 Public Key를 제공하도록 하는 P2PKH 방식이 더 보편적으로 사용되었다. P2PKH 또한 Unlocking Script에서 Public Key를 노출하기 때문에, UTXO를 소비할 때 이전의 동일한 비트코인 주소로 잔액 코인을 보내지 않는 것이 더 안전하다고 볼 수 있다.
P2PKH
P2PKH (Pay To Pubkey Hash)는 Public Key의 해시값에게 지불하는 스크립트로서, 여기서의 해시값은 RipeMD160(SHA256(PubkeyKey)) 함수를 통해 산출된다. Locking Script는 [OP_DUP] + [OP_HASH160] + [publicKeyHash] + [OP_EQUALVERIFY] + [OP_CHECKSIG] 로 되어 있으며, Unlocking Script는 [Signature] + [PublicKey] 로 되어 있다.
Unlocking Script와 Locking Script를 접목하면, 아래와 같은 스크립트가 된다.
(Signature크기) + Signature + (publicKey크기) + publicKey + OP_DUP(0x76) + OP_HASH160(0xa9) + (publicKeyHash크기) + publicKeyHash + OP_EQUALVERIFY(0x88) + OP_CHECKSIG(0xac)
위 스크립트를 해석하면, 먼저 Signature를 스택에 푸시하고, 다음으로 PublicKey를 푸시한다. 다음 스택의 top에 있는 PublicKey를 꺼내서 이를 Duplicate(복제)하여 스택에 넣는다. 이렇게 하면 스택 안에는 Signature + PublicKey + PublicKey 이 들어 있게 된다.
다음 스택 top에 있는 PublicKey를 꺼내고 이를 HASH160 으로 해싱하여 그 결과를 스택에 넣게 된다. 다음 publicKeyHash 크기만큼 publicKeyHash를 스택에 푸시하고, 스택 top에 있는 2개의 값(모든 해시값)을 꺼내 이 두개가 같은지(OP_EQUALVERIFY) 체크한다. 만약 다르면 에러를 내고 수행을 멈춘다. 이제 스택에는 Signature + PublicKey만이 남아 있게 되는데, OP_CHECKSIG은 두 개의 파라미터를 스택에서 꺼내 디지탈 서명을 확인한다. 만약 서명이 맞으면 true (OP_1)을 스택에 넣고 리턴한다. 스택에 OP_1이 남아 있으면 그 트랜잭션을 참(true)으로 여겨진다.
P2MS
P2MS (Pay To MultiSig)는 다중 공개키(Multiple Public Key)에게 지불하는 스크립트로서, 복수 개의 공개키에게 코인을 보내는 방식이다. 일반적으로 N개의 공개키들 중 M개가 승인하면 되는데, 예를 들어, 3개(N)의 공캐키 중 2개(M)의 공개키를 가진 소유자가 승인하면 되는 방식이다.
Locking Script는 [M] + [pubKey1] ~ [pubkeyN] + [N] + [OP_CHECKMULTISIG] 로 되어 있으며, Unlocking Script는 [OP_0] + [Signature1] ~ [SignatureM] 로 되어 있다. Unlocking Script의 첫부분은 OP_0 (0x00)은 기존 OP_CHECKMULTISIG에 있어 왔던 버그 때문에 붙여진 것이다. Locking Script에는 N개의 Public Key가 나열되고, Unlocking Script에는 (N보다 같거나 작은) M개의 Signature가 순차적으로(주: Public Key에 나열된 순서대로) 들어가게 된다. 만약 Unlocking Script에 나열된 Signature가 Locking Script의 Public Key순서와 다르면, 트랜잭션 오류가 발생한다.
3개의 Public Key에 대해 2개의 Signature가 맞으면 되는 경우, 아래와 같은 스크립트가 된다. 여기서 M, N의 값은 OP_1(0x51), OP_2(0x52), OP_3(0x53), ... 등의 값을 사용한다.
OP_0 + (Signature1크기) + Signature1 + (Signature2크기) + Signature2 + 2(0x52) + (pubKey1크기) + pubKey1 + (pubKey2크기) + pubkey2 + (pubKey3크기) + pubkey3 + 3(0x53) + OP_CHECKMULTISIG(0xae)
위 스크립트의 실행은 먼저 Signature1, Signature2를 차례로 스택에 넣게 되고, 다음 M(OP_2) 숫자를 스택에 넣는다. 다음 Public Key들을 차례로 스택에 넣고, N(OP_3) 숫자(데이타처럼 취급)를 스택에 넣는다. OP_CHECKMULTISIG 이전의 모든 데이타가 스택에 넣어진 상태에서, OP_CHECKMULTISIG을 실행하게 되는데, 여기서 N을 꺼내 3개의 Public Key를 꺼내게 되고, M을 꺼내 다시 2개의 Signature를 얻은 후, Signature를 순차적으로 Public Key와 함께 검증하게 된다. 만약 2개의 Signature가 맞으면, 트랜잭션은 참으로 성립한다.
P2SH
P2MS는 Locking Script에서 여러 개의 Public Key들을 계속 나열하게 되어 스크립트의 크기가 커지는 단점이 있는데, 이를 개선하기 위해 P2SH 스크립트가 사용되었다. P2SH (Pay To Script Hash)는 P2MS Locking Script를 해싱하여 해시값(Script Hash라 부름)을 얻은 후, 이 20바이트 Script Hash에 대해 지불하는 방식이다. 이렇게 20바이트 Script Hash를 사용하면 여러 송금자(sender)는 P2MS에서와 같이 긴 트랜잭션을 생성할 필요 없이 작은 크기의 트랜잭션을 생성할 수 있다. 예를 들어, 3개 중 2개의 키를 요구하는 경우 P2MS는 105바이트를 필요로하는 반면, P2SH는 23바이트만 필요하다.
Locking Script는 [OP_HASH160] + [ScriptHash] + [OP_EQUAL] 로 되어 있으며, Unlocking Script는 [OP_0] + [Signature1] ~ [SignatureM] + [Script] 로 되어 있다. Unlocking Script에서의 [Script]는 실제 MultiSig를 위한 비트코인 스크립트인데, 이 스크립트 전체를 하나의 데이타로 취급하여 Serialized 된 Script 데이타로 넣게 된다.
위의 P2MS에서의 예와 같이 3개 중 2개의 키가 맞으면 트랜잭션이 성립하는 예를 P2SH에서 사용하면 다음과 같은 스크립트가 실행된다.
OP_0 + (Signature1크기) + Signature1 + (Signature2크기) + Signature2 + Script[OP_2(0x52) + (pubKey1크기) + pubKey1 + (pubKey2크기) + pubkey2 + (pubKey3크기) + pubkey3 + OP_3(0x53) + OP_CHECKMULTISIG(0xae)] + OP_HASH160(0xa9) + ScriptHash + OP_EQUAL(0x87)
스크립트의 처음 OP_0은 P2MS에서 설명한 바와 같이 OP_CHECKMULTISIG의 버그로 넣어 준 것이고, 다음 2개의 Signature가 스택에 들어가게 된다. 다음 "Script"는 Serialized된 형태로 데이타처럼 전체가 스택에 넣어지게 된다. P2SH 스크립트는 특별하게 이렇게 Unlocking Script가 스택에 넣어진 후, 이 전체를 복사해서 또다른 스택에 넣에 둔다. 이는 두번째 스택의 내용은 나중에 실행된다.
다음 Locking Script에서 OP_HASH160은 스택 top에 있는 Serialized Script를 꺼내 SHA256/RipeMD160 해시함수를 거쳐 20바이트(160비트)의 해시를 계산해서 스택에 넣게 된다. 다음 ScriptHash가 스택에 넣어지고, OP_EQUAL은 스택에서 2개의 아이템을 꺼내 둘이 같은지 비교한다. 이 두개의 해시값이 동일하면, 이는 Unlocking Script의 "Script"의 해시값이 Locking Script의 "Script Hash"와 동일하다는 의미로서, 올바른 Unlocking Script임을 증명하게 된다.
위와 같이 Script에 대한 증명이 끝난 후, 이제 두번째 스택에서 Serialized Script를 꺼내 Deserialize하고 여기서 나온 스크립트를 실행하게 된다. P2MS에서와 같이 OP_2부터 OP_3 까지 차례로 스택에 푸시하고, OP_CHECKMULTISIG을 실행하기 된다. OP_CHECKMULTISIG는 OP_3를 보고 3개의 pubKey를 꺼내고, OP_2를 보고 2개의 Signature를 꺼내 Signature 타당성을 검증하게 된다.