비트코인 사용자는 자신의 자금이 어떻게 사용되는지 관리하는 규칙을 선택하고 네트워크가 자동으로 자금을 운용하도록 할 수 있습니다. 이러한 프로그래밍 방식을 통해 사용자는 다중 서명 같은 단순한 정책부터 무신뢰 거래 같은 복잡한 정책까지 자신만의 정책을 설정할 수 있습니다. 그러나 일부 정책은 현재 비트코인 시스템에서 분명한 표현을 갖고 있지 않습니다. 이 글에서는 이러한 예 중 하나인 약정(covenant)에 대해 이야기하고 Elements에 이미 존재하는 비트코인 스크립트 언어를 약간 확장하여 약정을 구현할 수 있는 방법을 설명할 것입니다.
금융 약정은 자금(일반적으로 대출)의 사용 방법을 제한하는 계약입니다. 비트코인 스크립트 언어는 누가 특정 자금을 사용하는 거래를 생성할 수 있는지 제한하는 인증 체계를 허용하도록 프로그래밍될 수 있습니다. 그러나 인증 요건을 충족하는 당사자라면 누구나 해당 자금을 사용한 거래를 생성할 수 있습니다. 약정을 통해, 인증 체계를 시행할 수 있을 뿐만 아니라 자금이 사용되는 거래의 종류를 제한할 수 있는 스크립트를 만들 수 있습니다. 예를 들어, Möser-Eyal-Sirer 금고를 사용하면 거래에 타임락을 걸고 자금에 대한 악의적인 접근을 감지할 수 있습니다.
비트코인과 마찬가지로, Elements Alpha 스크립트 언어는 거래 데이터에 직접 접근할 수 없습니다. 거래 데이터는 OP_CHECKSIG 및 OP_CHECKSIGVERIFY 작업을 통해 간접적으로만 접근 가능합니다. 이러한 작업에는 타원 곡선 공개 키와 디지털 서명이 서명 해시 유형과 함께 입력 값으로 사용됩니다. 지정된 서명 해시 유형에 따라 수정된 거래 데이터의 이중 SHA-256 해시를 계산한 뒤 디지털 서명 및 공개 키로 이중 해시 된 메시지 데이터의 디지털 서명을 검증합니다. 이 작업이 성공하면 OP_CHECKSIG는 스택에서 1의 값을, 실패할 경우 0을 반환합니다.
Elements Alpha의 새로운 OP_CHECKSIGFROMSTACK 및 OP_CHECKSIGFROMSTACKVERIFY 작업은 세 가지 입력 값(타원 곡선 공개 키, 메시지, 디지털 서명)을 받습니다. 이 작업은 메시지의 단일 SHA-256 해시를 수행한 뒤 디지털 서명과 공개 키로 해시 된 메시지 데이터의 디지털 서명을 검증합니다. 이 작업이 성공하면 OP_CHECKSIGFROMSTACK은 스택에서 1의 값을, 실패할 경우 0을 반환합니다.
Elements Alpha에서 약정을 생성하는 비결은 ‘OP_CHECKSIG 작업의 성공은 공개 키와 디지털 서명이 함께 거래 데이터에 대한 확약을 형성한다는 것을 의미한다’는 사실을 이용하는 것입니다. 성공적인 OP_CHECKSIGFROMSTACK 작업에서 동일한 공개 키와 디지털 서명이 사용된다면 OP_CHECKSIGFROMSTACK에 전달된 메시지는 반드시 거래 데이터와 동일해야 합니다. 우리는 OP_CHECKSIG와 OP_CHECKSIGFROMSTACK을 함께 사용하여 OP_CHECKSIGFROMSTACK에 전달된 메시지가 서명된 거래 데이터라는 암호화 보장을 생성합니다.
약정의 구성
아래의 스크립트를 생각해 봅시다.
- OP_OVER OP_SHA256 <pubKey>
- 2 OP_PICK 1 OP_CAT OP_OVER
- OP_CHECKSIGVERIFY
- OP_CHECKSIGFROMSTACKVERIFY
처음에 두 개의 항목이 스택에 있다고 가정해 봅시다.
</code> {:.ui.segment} 1단계 이후, SHA-256을 사용하여 ``를 한 번 해시하고 공개 키를 스택에 푸쉬했습니다. <sha256(sigTransactionData)></code> {:.ui.segment} 2단계 이후, 해당 서명과 공개 키를 스택의 프론트에 복사했습니다. 서명에는 `0x01` 바이트가 추가되어 있습니다. 이것은 `SIGHASH_ALL` 해시 유형에 대한 코드입니다. 서명된 거래 데이터에 모든 입출력값이 포함된다는 것을 명시합니다. <signature;SIGHASH_ALL> <sha256(sigTransactionData)></code> {:.ui.segment} 3단계의 `OP_CHECKSIGVERIFY`는 유효한 서명이 제공되었다는 것을 확인하고 거래가 개인 키로 승인되었음을 증명합니다. <sha256(sigTransactionData)> </code> {:.ui.segment} 4단계의 `OP_CHECKSIGFROMSTACKVERIFY`는 동일한 pubKey와 서명을 사용합니다. 이 작업은 다른 SHA-256을 수행하고 이중 해시 된 ``에서 디지털 서명 유효성 검사를 수행합니다. 이전의 `OP_CHECKSIGVERIFY` 작업에서 사용한 것과 완전히 동일한 pubKey와 서명을 사용할 것이기 때문에 이 `OP_CHECKSIGFROMSTACKVERIFY`는 ``가 3단계의 `OP_CHECKSIGVERIFY` 작업 중 확인된 메시지와 동일한 경우에만 성공할 수 있습니다. </code> {:.ui.segment} 스크립트의 이 시점에서 ``가 서명된 거래 데이터의 정확한 사본이라는 것을 확신할 수 있습니다. 우리는 거래 데이터에 대한 완전한 접근 권한을 통해 약정을 시행하기 위한 스크립트 작업을 더 추가할 수 있습니다. 출력 수를 제한할 수 있습니다. 출력 값을 제한할 수도 있습니다. 그리고 출력의 스크립트를 제한할 수 있습니다. 약정에 대한 이 방법에는 몇 가지 한계점이 있습니다. Elements Alpha 스크립트에서 각 스택 항목은 520 바이트로 제한됩니다. 즉, 우리는 ``에 대한 약정을 520 바이트 내에서만 만들 수 있습니다. 그러나 520 바이트 내에서도 흥미로운 약정을 만들 수 있습니다. ## 재귀적 약정(Recursive Covenants) 이 섹션에서는 간단한 재귀적 약정을 만들 것입니다. 이 약정은 출력 스크립트가 입력 스크립트와 동일한 단일 출력에 사용되는 거래로만 거래를 제한할 것입니다. 이 약정은 단순하지만 재귀적 약정을 구축하는 기본적인 방법을 보여줍니다. 나중에 더 흥미로운 약정을 만들어 볼 것입니다. 이 스크립트는 서명된 거래 데이터 전체를 스택에 넣고 분석하는 것이 아니라 거래 데이터 조각조각을 번역할 것입니다. 그러면 스크립트는 약정을 시행하기 위해 각 데이터 조각에 조건을 부여할 수 있습니다. 각 데이터 조각의 크기가 알려진 고정된 크기여야 한다는 것이 중요한데, 그렇지 않으면 아주 길거나 아주 짧은 데이터 조각의 공격을 받아 스크립트가 거래 데이터의 한 필드를 분석할 때 다른 필드를 분석하는 것으로 착각할 수도 있습니다. 다음은 우리가 만든 간단한 약정에 대한 scriptPubKey입니다. 1. `<0x0100000001>` 1. `OP_SWAP` `OP_SIZE` `36` `OP_NUMEQUALVERIFY` `OP_CAT` 1. `<0x00>` `OP_CAT` 1. `OP_SWAP` `OP_SIZE` `32` `OP_NUMEQUALVERIFY` `OP_CAT` 1. `<0x00005f>` `OP_CAT` 1. `2` `OP_PICK` `OP_SIZE` `95` `OP_NUMEQUALVERIFY` `OP_CAT` 1. `<0xffffffff0100>` `OP_CAT` 1. `OP_SWAP` `OP_SIZE` `32` `OP_NUMEQUALVERIFY` `OP_CAT` 1. `<0x0000>` `OP_HASH256` `OP_CAT` 1. `<0x17a914>` `OP_CAT` 1. `OP_SWAP` `OP_HASH160` `OP_CAT` 1. `<0x870000000001000000>` `OP_CAT` 1. `OP_SHA256` 1. `1` `OP_DUP` `OP_CAT` `OP_DUP` `OP_CAT` `OP_DUP` `OP_CAT` `OP_DUP` `OP_CAT` `OP_DUP` `OP_CAT` `OP_DUP` `OP_CAT` 1. `OP_DUP` 1. `2` `OP_ROLL` `3` `OP_PICK` 1. `OP_CHECKSIGFROMSTACKVERIFY` 1. `1` `OP_CAT` `OP_SWAP` 1. `OP_CHECKSIG` 이를 사용하기 위한 해당 scriptSig는 1입니다. `` 1. `