관리 메뉴

개발이야기

[Mastering Bitcoin] 마스터링 비트코인 내 맘대로 정리 - Ch07 본문

블록체인 /마스터링 비트코인

[Mastering Bitcoin] 마스터링 비트코인 내 맘대로 정리 - Ch07

안성주지몬 2019. 1. 28. 00:00

Ch7. Advanced Transactions and Scripting


Introduction

이전 장에서는 비트코인 트랜잭션의 기본 개념을 소개하고 가장 일반적인 트랜잭션 스크립트 인 P2PKH 스크립트를 살펴보았다. 이번 장에서는 고급 스크립트 작성 방법과 복잡한 조건이 있는 트랜잭션을 작성하는 방법에 대해 살펴보겠다.

Multisignature

기존의 비트코인 시스템은 하나의 주소에 하나의 개인키가 연결된 단일서명(Single-signature) 거래 방식이지만 다중서명(Multisignature) 방식은 하나의 주소에 n개의 개인키가 설정되어 있다. 이 다중서명 주소에서 인출을 할 때는 n개의 개인키 중에서 m개의 서명이 있어야 가능하다. 이를 M-of-N 거래라고도 알려져 있으며, 여기서 N은 전체 키의 개수이고 M은 유효성 검사에 필요한 서명 임계값(한계값)이다. 이는 보안을 강화하거나 온라인 거래 시 소비자를 보호하는 방안 등 다양하게 활용할 수 있다.  


M-of-N 다중서명 조건을 설정하는 잠금 스크립트의 일반적인 형태는 다음과 같다.


2-of-3 다중 서명 조건을 설정하는 잠금 스크립트는 다음과 같다.


위의 잠금 스크립트는 서명과 공개키의 쌍을 포함하는 잠금 해제 스크립트로 충족될 수 있다.



*참고

잠금 스크립트는 출력(output)에 배치되는 지출 조건이다. 나중에 출력을 사용하기 위해 충족되어야하는 조건을 지정한다. 잠금 스크립트는 공개키 또는 비트코인 주소(공개키 해시)가 포함되어 있기 때문에 scriptPubKey 라고 한다.

잠금 해제 스크립트는 잠금 스크립트에 의해 출력에 설정된 조건을 해결하거나 만족시키는 스크립트로, 출력(output)을 사용한다. 잠금 해제 스크립트는 모든 트랜잭션 입력(input)의 일부다. 대개의 경우 개인키로 사용자의 지갑에서 생성된 디지털 서명이 들어 있다. 이렇듯 디지털 서명이 포함되어 있기 때문에 scriptSig라고 불린다.

비트코인 유효성을 검사하는 노드는 잠금 및 잠금 해제 스크립트를 함께 실행하여 트랜잭션의 유효성을 검사한다. 각 입력에는 잠금 해제 스크립트가 포함되어 있으며 기존의 UTXO를 참조한다. 유효성 검사 소프트웨어는 잠금 해제 스크립트를 복사하고 입력으로 참조되는 UTXO를 검색 한 다음 해당 UTXO에서 잠금 스크립트를 복사한다. 잠금 해제 및 잠금 스크립트가 순서대로 실행된다. 잠금 해제 스크립트가 잠금 스크립트 조건을 충족하면 입력이 유효해진다.

A bug in CHECKMULTISIG execution

CHECKMULTISIG가 실행될 때 스택의 M + N + 2 개의 항목을 매개 변수로 사용해야 한다. 그러나 버그 때문에 CHECKMULTISIG는 하나 이상의 추가 값을 POP한다.

앞의 유효성 검증 예제를 사용하여 자세히 살펴보자.

우선, CHECKMULTISIG는 맨 위 항목인 N을 pop한다(여기서는 3). 그런 다음 서명할 수 있는 공개키인 N개의 항목을 표시한다(Public Key A, B, C). 그런 다음 하나의 항목인 M, 즉 몇 개의 서명이 필요한지를 표시한다(여기서는 2). 이 시점에서 CHECKMULTISIG는 서명인 마지막 M개의 항목(Signature B, C)을 표시하고 유효한지 확인해야 한다. 그런데 여기서, 버그 때문에 CHECKMULTISIG가 한 개 이상의 추가항목 (총 M + 1)을 가져오게 된다. 추가 항목은 서명을 검사할 때 CHECKMULTISIG 자체에 직접적인 영향을 미치지 않으므로 무시된다. 그러나 CHECKMULTISIG가 없으면 CHECKMULTISIG가 빈 스택을 pop하려고 시도 할 때 스택 오류 및 스크립트 실패 (트랜잭션을 유효하지 않은 것으로 표시)가 발생하므로 추가 값이 있어야 한다.








Pay-to-Script-Hash(P2SH)

다중서명 스크립트는 강력한 기능이지만 사용하기가 까다롭다. 다중서명 스크립트에서 사용자는 지불 전에 모든 고객(상대방)에게 이 스크립트를 전달해야 한다. 각 고객은 사용자 지정 트랜잭션 스크립트를 만들 수 있는 특별한 비트코인 지갑 소프트웨어를 사용해야하며, 각 고객은 사용자 지정 스크립트를 사용하여 트랜잭션을 만드는 방법을 이해해야 한다. 또한 이 스크립트에는 매우 긴 공개키가 포함되므로 트랜잭션은 간단한 지불 트랜잭션보다 약 5배 더 커진다. 그 초대형 거래의 부담은 수수료 형태로 고객이 부담하게 된다. 마지막으로, 이와 같은 대형 트랜잭션 스크립트는 모든 전체 노드의 RAM에 설정된 UTXO에서 소비될 때까지 전달된다. 즉, 다중서명 스크립트를 이해하고 사용하는 것이 어렵다는 말이다....!

P2SH는 이러한 실질적인 어려움을 해결하고 복잡한 스크립트를 비트코인 주소로 지불하는 것처럼 쉽게 사용할 수 있도록 개발되었다. P2SH 결제를 사용하면 복잡한 잠금 스크립트(ScriptSig)가 디지털 지문(암호화된 해시)으로 바뀐다. UTXO를 소비하려는 트랜잭션은 잠금 해제 스크립트(ScriptPubKey) 외에도 해시와 일치하는 스크립트를 포함해야 한다. 간단히 말하면 P2SH는 "이 해시와 일치하는 스크립트에 비용을 지불해야 한다. 이 스크립트는 나중에 이 출력이 소비될 때 제공된다."



P2SH가 없는 복잡한 스크립트



P2SH가 있는 복잡한 스크립트

위에서 볼 수 있듯이 P2SH를 사용하면 사용 스크립트(redeem script), 지출 조건을 자세히 설명하는 복잡한 스크립트가 잠금 스크립트에 표시되지 않는다. 대신 해시 값만 잠금 스크립트에 포함되며 출력물을 소비할 때 잠금 해제 스크립트에 지출 스크립트 자체가 나중에 표시된다. 이것은 수수료와 복잡성의 부담을 송금인으로부터 수령인으로 이동시킨다.



위의 P2SH에 대한 설명을 이해하기 쉽도록 그림으로 나타낸 것



*참고

아래의 그림은 기본 P2SH 워크 플로이다. Bob은 원하는 스크립트로 사용 스크립트(redeem script)를 생성하고 사용 스크립트를 해시하여 Alice에 사용 스크립트 해시를 제공한다. Alice는 Bob의 사용 스크립트 해시가 포함된 P2SH 스타일 출력(TX1)을 만든다.




Bob은 출력을 보내려고 할 때 서명 스크립트(Signature Script)의 전체 사용 스크립트와 함께 자신의 서명을 제공한다. P2P 네트워크는 Alice가 출력(TX1)에 넣은 스크립트 해시와 동일한 값으로 Pubkey Script인 것처럼 스크립트를 처리하여 검색 스크립트(CHECKMULTISIG)가 false를 반환하지 않으면 Bob에게 출력을 보낸다.

사용 스크립트의 해시는 pubkey 해시와 동일한 속성을 갖기 때문에 표준 주소와 구분하기 위해 하나의 작은 변경만으로 표준 비트코인 주소 형식으로 변환 할 수 있다.



Benefits of P2SH
• 복잡한 스크립트를 트랜잭션 출력(결과)에서 ​​더 짧은 스크립트로 바뀌어 트랜잭션 크기를 작게 만든다.

• 스크립트는 비트코인 주소로 코딩될 수 있으므로 발신자와 발신자의 지갑에는 P2SH 구현을 위한 복잡한 엔지니어링이 필요하지 않다.

• P2SH는 스크립트를 작성하는 부담을 보낸 사람이 아닌 받는 사람에게 이동시킨다.

• P2SH는 긴 스크립트의 데이터 저장에 대한 부담을 출력(블록체인에 저장된 것은 UTXO 세트에 있음)에서 입력(블록체인에만 저장됨)으로 이동한다.

• P2SH는 긴 스크립트의 데이터 저장에 대한 부담을 현재 시간(지불)에서 미래 시간(지출 시점)으로 이동시킨다.

• P2SH는 긴 스크립트의 거래 수수료 비용을 보낸 사람에서 받는 사람에게 옮긴다. 긴 스크립트를 사용하려면 받는 사람에게 보내야한다.





Timelocks

Timelocks는 특정 시점 이후의 지출만 허용하는 트랜잭션 또는 출력에 대한 제한사항이다.

Transaction Locktime (nLocktime)

비트코인은 처음부터 트랜잭션 수준의 타임 록(잠금 시간) 기능을 갖추고 있다. 비트코인 Core 소스 코드에서 nLockTime이라고 한다. nLockTime은 대부분의 트랜잭션에서 0으로 설정되어(0은 잠금이 안되어 있다는 의미) 즉시 전파 및 실행을 나타낸다. 잠금 시간은 트랜잭션을 블록체인에 추가할 수 있는 가장 빠른 시간을 나타낸다.

nLocktime이 5억보다 작으면(미만) 잠금 시간이 블록 높이로 분석된다. 트랜잭션은 이 높이 이상의 블록에 추가 될 수 있다. (트랜잭션이 차단 높이가 될 때까지 유효하다고 판단되지 않으면 전파되지 않음)

nLocktime이 5억보다 크거나 같으면, 잠금 시간은 Unix의 신기원 시간 형식(Jan-1-1970년 이후 현재까지 경과된 초 수 - 현재 1.395억을 초과)을 사용하여 구문 분석되어 지정된 시간까지 유효하다고 판단되지 않으면 전파되지 않는다.

Transaction locktime limitations

Alice는 그녀의 출력 중 하나를 Bob의 주소로 보내는 트랜잭션에 서명하고 트랜잭션 nLocktime을 향후 3개월로 설정한다. Alice는 Bob에게 보류할 트랜잭션을 보낸다. 이 거래를 통해 Alice와 Bob은 다음과 같은 사실을 알고 있다.

- Bob은 3개월이 경과할 때까지 자금을 사용하기 위해 거래를 전송할 수 없다.

- Bob은 3개월 후에 거래를 전송할 수 있다.

하지만,

- Alice는 locktime없이 같은 입력을 두 번 소비하는 또 다른 트랜잭션을 생성 할 수 있다. 따라서 Alice는 3개월이 지나기 전에 동일한 UTXO를 보낼 수 있다.

- Bob은 Alice가 그렇게 하지 않을 것이라는 보장이 없다.(3개월 후에 반드시 이 거래가 보장된다는 확신이 없다)

트랜잭션 nLocktime의 한계를 이해하는 것이 중요하다. 확실한 것은 Bob이 3개월이 경과하기 전에는 사용할 수 없다는 것이다. 또한 Bob이 자금을 확보한다는 보장은 없다. 이러한 보장을 달성하려면 timelock 제한을 UTXO 자체에 배치해야 하며 트랜잭션이 아닌 잠금 스크립트의 일부가 되어야 한다. 이것은 Check Lock Time Verify라고 하는 다음 형태의 타임 록에 의해 달성된다.

[참고 사이트] https://medium.com/@LinkBlock/5-3-%EB%B9%84%ED%8A%B8%EC%BD%94%EC%9D%B8%EC%9D%98-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B5%AC%EC%A1%B0-54aa0fb6b04f



Check Lock Time Verify (CLTV)

CLTV는 nLocktime의 경우처럼 트랜잭션 당 타임 록이 아닌 출력마다 타임 록이다. 이렇게 하면 타임 록이 적용되는 방식에 훨씬 더 큰 유연성을 부여 할 수 있다.

간단히 말하면, 출력의 redeem 스크립트에 CLTV opcode를 추가하면 출력을 제한하므로 지정된 시간이 경과한 후에 만 ​​사용할 수 있다.(특정 UTXO를 nLocktime이 더 크거나 같은 값으로 설정된 후의 트랜잭션에서만 사용할 수 있도록 제한)

CLTV opcode는 하나의 매개 변수를 입력으로 사용하며 nLocktime과 같은 형식으로 숫자로 표시된다. VERIFY 접미사에 표시된 대로 CLTV는 결과가 거짓인 경우 스크립트 실행을 중단하는 opcode 유형이다. 결과가 TRUE이면 실행이 계속된다.

CLTV로 출력을 잠그려면 출력을 생성하는 트랜잭션의 출력 스크립트를 사용한다. 예를 들어 Alice가 Bob의 주소로 지불하는 경우 출력에는 일반적으로 다음과 같은 P2PKH 스크립트가 포함된다.


지금부터 3 개월 후에 이 트랜잭션을 잠그려면 트랜잭션을 P2SH 트랜잭션으로 바꾸고 다음과 같은 사용 스크립트를 사용한다.

여기서 <now + 3 months>는 트랜잭션이 채굴된 시점으로부터 3개월 후 예상 블록 높이 또는 시간 값이다.


Bob은 이 UTXO를 사용하려고 할 때 UTXO를 입력으로 참조하는 트랜잭션을 생성한다. 그는 해당 입력의 잠금 해제 스크립트에서 서명과 공개키를 사용하는데, 이 스크립트에 트랜잭션 nLocktime을 CHECKLOCKTIMEVERIFY 매개 변수 앨리스 세트의 타임 록과 동일하거나 더 크게 설정한다. 그런 다음 Bob은 비트코인 네트워크에서 트랜잭션을 브로드 캐스트한다.

Bob의 거래는 다음과 같이 평가됩니다. CHECKLOCKTIMEVERIFY 매개 변수 Alice 세트가 지출 트랜잭션의 nLocktime보다 작거나 같으면 스크립트 실행이 계속 된다. 그렇지 않으면 스크립트 실행이 중단되고 트랜잭션은 유효하지 않은 것으로 간주된다.

더 자세히 말하면, 다음의 경우에 CHECKLOCKTIMEVERIFY는 실패하고 트랜잭션을 무효로 표시하여 실행을 중지한다.

1. 스택이 비어있다.

2. 스택의 맨 위 항목이 0보다 작다.

3. 최상위 스택 항목의 잠금 시간 유형과 nLocktime 필드가 동일하지 않다.

4. 최상위 스택 항목이 트랜잭션의 nLocktime 필드보다 크다.

5. 입력의 nSequence 필드는 0xffffffff이다.


*참고

트랜잭션의 주요 필드 내용은 아래와 같다.


Input 목록 및 Output 목록은 크기가 1~9로 되어있지만, 이는 가변적인 길이의 정수이며, 데이터 길이를 결정하는 프리픽스가 정수 앞에 부여된다. 값의 범위당 바이트수와 타입은 아래와 같다.



검사 후, CLTV가 만족되면 그 앞에 있던 시간 매개 변수는 스택의 최상위 항목으로 남게 되며 후속 스크립트 opcode의 올바른 실행을 위해 DROP과 함께 삭제해야 할 수 있다.



Relative Timelocks

nLocktime과 CLTV는 절대적인 시간을 지정한다는 점에서 절대적인 timelocks이다.

상대적인 timelocks는 UTXO가 블록체인에 기록될 때까지 시계가 계산을 시작하지 않는다.

절대 시간 잠금과 같은 상대 시간 잠금은 트랜잭션 수준 기능과 스크립트 수준 opcode로 구현된다. 각 트랜잭션에서 각 입력에 nSequence 필드를 설정함으로써 상대 시간 잠금을 트랜잭션의 입력에 설정할 수 있다.

nSequence 값이 2^31 미만인 트랜잭션 입력은 상대적 시간 잠금으로 해석된다. 이러한 트랜잭션은 입력이 상대적 시간 잠금 값만큼 경과한 후에만 ​​유효하다. 예를 들어, 30 블록의 nSequence 상대 시간 잠금이 있는 하나의 입력을 가진 트랜잭션은 입력에서 참조된 UTXO가 마이닝된 때로부터 적어도 30 블록이 경과 한 경우에만 유효하다.

nSequence 값은 블록 또는 초 단위로 지정되지만 nLocktime에서 사용한 것과 약간 다른 형식으로 지정된다. type-flag는 블록을 계산하는 값과 초 단위로 시간을 계산하는 값을 구별하는 데 사용된다. type-flag는 23번째(왜 23번째?) 최하위 비트에 설정된다. type-flag가 설정되면 nSequence 값은 512초의 배수로 해석된다. type-flag가 설정되지 않으면 nSequence 값이 블록 수로 해석된다.

nSequence를 상대 타임 록으로 해석할 때, 최하위 16비트만 고려된다. 일단 플래그 (비트 32 및 23)가 평가되면 nSequence 값은 일반적으로 16비트 마스크로 "마스크"된다.

무슨 말인지 하나도 모르겠네




Median-Time-Past

비트코인은 분산된 네트워크로서, 각 참여자는 자신의 시간(시각)을 가진다. 네트워크상의 이벤트가 모든 곳에서 즉각적으로 발생하지는 않는다. 네트워크 대기 시간은 각 노드의 관점에서 고려되어야 한다. 결국 모든 항목이 동기화되어 공통 원장을 만든다. 비트코인은 과거에 존재했던 원장 상태에 대해 10분마다 합의에 이른다.

블록 헤더에 설정된 타임 스탬프는 마이너가 설정한다. 그러나 이것은 마이너들이 한 블록 내의 시간에 대해 거짓말을 하여 시간 잠금 트랜잭션을 포함시켜 추가 비용을 지불하는 등의 행동을 할 수도 있다. 자세한 내용은 다음 장 참조.

Median-Time-Past는 마지막 11블록의 타임 스탬프를 취하여 중간값을 찾아 계산한다. 그 중간 시간은 일치 시간이 되며 모든 시간 기록 계산에 사용된다. 11개의 블록을 통합함으로써, 아직 완벽하지 않은 타임 록과의 거래에서 수수료를 얻기 위해 어떤 마이너도 타임 스탬프에 영향을 줄 수 없다.

Scripts with Flow Control (Conditional Clauses)

비트코인 Script의 강력한 기능 중 하나는 조건부 절이라고도 하는 흐름 제어이다. IF, THEN, ELSE 등의 다양한 구문을 사용한다.

또한 비트코인 조건부 표현식은 무한정 "중첩"될 수 있다. 즉, 조건부가 다른 조건을 포함할 수 있음을 의미한다. 비트코인 스크립트 흐름 제어는 수백 또는 수천 개의 가능한 실행 경로가 있는 매우 복잡한 스크립트를 작성하는 데 사용될 수 있다. 중첩에는 제한이 없지만 합의 규칙은 스크립트의 최대 크기(바이트)를 제한한다.

비트코인은 IF, ELSE, ENDIF 및 NOTIF opcode를 사용하여 흐름 제어를 구현하고, 조건부 표현식에는 BOOLAND, BOOLOR, NOT과 같은 부울 연산자가 포함될 수 있다.

언뜻 보기에 비트 코인의 흐름 제어 스크립트는 혼란스러울 수 있다. 비트코인 Script는 스택 언어이기 때문이다. 1 1 ADD로 표현할 때 1 + 1이 역방향으로 보이는 것과 같은 방식으로 비트코인의 흐름 제어절도 역방향으로 보인다.



대부분의 프로그래밍 언어에서 흐름 제어의 의사 코드



비트코인 Script 흐름 제어






Complex Script Example

이 예에서는 두바이에 있는 회사 소유주인 수출입 업무를 담당하는 Mohammed의 이야기를 사용하겠다.

이 예에서 Mohammed는 유연한 규칙을 사용하여 회사 자본 계정을 구성하고자 한다. 그가 만드는 구성표에는 시간 경과에 따라 다양한 권한이 필요하다. multisig(다중서명) 계획의 참가자는 모하메드, 그의 두 파트너 Saeed와 Zaira, 그리고 그들의 회사 변호사 Abdul이다. 세 파트너는 다수 규칙에 따라 의사 결정을 내리므로 세 사람 중 두 사람이 동의해야한다. 그러나 열쇠에 문제가 있는 경우 변호사는 세 파트너 서명 중 하나를 사용하여 자금을 회수 할 수 있다. 마지막으로, 모든 파트너가 이용할 수 없거나 잠시 동안 능력이 없을 경우, 변호사가 직접 계정을 관리 할 수 ​​있다.

다음은 모하메드가 이것을 달성하기 위해 고안한 redeem script(사용 스크립트)이다.

Mohammed의 스크립트는 중첩된 IF ... ELSE 흐름 제어 절을 사용하여 세 개의 실행 경로를 구현한다.



첫 번째 실행 경로에서 이 스크립트는 세 파트너와 함께 간단한 2-of-3 다중서명으로 작동한다(3행~9 행). 3 행은 multisig의 쿼럼을 2(2/3)로 설정한다. 이 실행 경로는 잠금 해제 스크립트 끝 부분에 TRUE TRUE를 입력하여 선택할 수 있다.

첫 번째 실행 경로 (2-of-3 multisig)에 대한 스크립트 잠금 해제



두 번째 실행 경로는 UTXO 작성 후 30일이 경과한 후에 만 ​​사용할 수 있다. 그 당시 변호사와 3명의 파트너 중 한 명인 Abdul이 서명해야한다(1-of-3 multisig). 이것은 multisig에 대한 쿼럼을 1로 설정하는 7 행에 의해 수행된다. 이 실행 경로를 선택하려면 잠금 해제 스크립트가 FALSE TRUE로 끝난다.

두 번째 실행 경로에 대한 스크립트 잠금 해제 (변호사 +1)


마지막으로, 세 번째 실행 경로는 압둘 (Abdul) 변호사가 자금을 혼자서 보낼 수 있긴 하지만, 90 일 후에만 ​​가능하다. 이 실행 경로를 선택하려면 잠금 해제 스크립트가 FALSE로 끝나야한다.

세 번째 실행 경로에 대한 스크립트 잠금 해제 (변호사 전용)



Comments