Tapscript: 새로운 Opcode, 제한 축소와 약정
Liquid Network Blockstream Research

Tapscript: 새로운 Opcode, 제한 축소와 약정

Andrew Poelstra

소개

이번 포스팅은 Taproot 출시의 일부로 Liquid에 배포한 개선 사항에 대한 시리즈의 세 번째이자 마지막 글입니다. 이전 두 포스팅에서는 Taproot 자체 부분 서명된 Elements 트랜잭션에 대해 얘기했습니다. 이번 포스팅에서는 Elements Script에 도입한 새로운 opcode를 설명하고, 그것들이 무엇에 사용될 수 있는지, 그리고 우리가 그것으로 무엇을 하고 있는지 다루겠습니다.

이 opcode 세트는 2015년 Elements Alpha 출시 이후 Script에 실행된 주요 개선 세트입니다. 우리 연구팀은 또한 MuSig, Elements 블록체인, Lightning Network에 대한 상당한 성과와 Simplicity에 대한 지속적인 노력을 통해 Bitcoin 관련 응용 암호화에 대한 혁신을 이뤄내고 있습니다. 특히 우리는 비트코인의 Taproot에 크게 기여했으며, 그에 따라 다음과 같은 Elements Script의 일부 변경 사항이 발생했습니다.

  • Opcode 갯수와 스크립트 크기에 대해 고정된 숫자 제한 제거
  • ECDSA 서명을 Schnorr로 교체
  • CHECKMULTISIG를 CHECKSIGADD로 대체하여 여러 서명의 일괄 검증 가능
  • 수많은 OP_SUCCESS opcode를 통해 기존 NOP opcode 대비, 네트워크로 소프트 포크할 수 있는 opcode 세트 확대

또한 Script가 코인 또는 기타 자산에 조건을 부여하는 ‘스마트 계약’을 구성하기 위해 지불 흐름을 제한할 수 있는 기능을 제공하는 약정(covenant)에 관해서도 얘기해 보겠습니다. 비트코인에 대한 약정은 수년에 걸쳐 다양한 형태로 제안되었지만 Elements에는 처음부터 존재했습니다. 우리는 Tapscript를 통해 기존의 약정 구현에서 개발자 피드백을 얻었고, 약정을 보다 잘 표현하고 효율적이며 인체 공학적으로 만들 수 있는 새로운 opcode를 도입했습니다.

마지막으로, 우리는 이러한 opcode를 사용하여 약정 가능 스크립트를 생성하는 비트코인의 Miniscript 언어의 확장판인 Elements Miniscript에 대해 간략하게 이야기하겠습니다. 현재 빠르게 개발 중으로 Liquid에 새로운 애플리케이션을 도입하게 되어 기쁩니다. 이 주제는 추후에 더 자세히 다룰 예정입니다.

약정과 자산

약정은 2013년 Greg Maxwell이 비트코인에 대해 반 농담식으로 처음 제안했는데, 사용자에게 흥미롭게 나쁜 애플리케이션을 생각해 보라고 했습니다. 이러한 나쁜 아이디어는 생각하기엔 재미있지만, 이로 인해 불행히도 오늘날까지 약정이라는 개념이 평가절하되고 있는 것 같습니다. 특별히 실질적이거나 심지어 약정에 특정한 문제가 아닌 경우에도 말입니다. 이러한 위험 추론과 같은 맥락으로, 약정은 비트코인 세계에서 아직 뜨거운 논쟁 거리로 남아 있습니다.

비트코인에서 최근에 가장 인기 있는 약정 제안은 잠재적인 논쟁을 피하기 위해 의도적으로 기능을 축소한 OP_CTV입니다.

우리는 이러한 두려움이 크게 과장됐다고 보는데요. 두려움은 약정이 기존 비트코인을 ‘오염’시키는 데 사용될 수 있다는 아이디어에서 비롯됩니다. 예를 들어 사용하기 위해 제3자 서명을 필요로 하는 것과 같은 ‘오염’은 코인 공급 전체에 퍼져 제거가 불가능할 것입니다. 그러나 이러한 방식은 2-of-2 ‘약정’ 제한을 유지하지 않는 거래에 한 당사자가 서명을 거부하는 2-of-2 CHECKMULTISIG 스크립트를 사용하는 방식으로 오늘날 실제로 구현할 수 있습니다. 하지만 아무도 이를 구현하지 않은 데는 그만한 이유가 있습니다. 이러한 코인은 분명히 일반 코인 대신 사용자가 수락할 수 없으며 일반 코인을 보관할 수탁자 또는 기타 법적 의무가 있는 사용자가 수락할 수 없습니다. 또한, 이렇게 생성된 코인은 추가 가중치로 인해 더 높은 네트워크 수수료를 지불해야 하고 지불에 사용할 수 있는 지갑의 복잡성을 가중시킵니다.

약정으로 위와 같은 시도를 하는 것은 훨씬 더 많은 비용과 지갑 복잡성이 요구되기 때문에 아무도 이러한 구현에는 관심이 없을 것이며 사용자가 거부할 것입니다.

약정이 무해하다는 추가 증거는 약정은 2018년 도입된 이후 Liquid Network에 계속 존재했으며 시스템 전체에 바이러스 약정이 확산되지 않았다는 것입니다. (그러나 뒤에 설명하겠지만 일반적으로 Liquid에서 약정 활용도가 낮은 데는 기술적인 이유가 있습니다.)

약정은 금고 또는 속도 제한과 같이 비트코인에 새로운 기능을 도입하여 사용자에게 코인을 보관하는 방식에 더 많은 유연성을 제공합니다.

자산 지원을 발행한 Elements에서는 약정이 제공하는 기능이 훨씬 더 뛰어납니다. 다음이 가능합니다.

  • 무제한 지정가 주문 또는 기타 알고리즘 거래를 구축
  • 옵션과 같은 금융파생상품 개발
  • 다른 계약에서 기능을 활성화하는 NFT인 ‘제어 토큰’ 생성

풍부한 연산 opcode세트가 있는 다중 자산 시나리오에서 우리는 이더리움과 경쟁업체의 기능 세트를 일치시킬 수 있고, 튜링 완전성을 피함으로써 강력한 형태의 분석을 구현할 수 있어야 합니다. 그러나 실제로는 이보다도 더 복잡합니다.

Elements와 비트코인 ​​스크립트

Elements는 2015년에 추가 기능이 포함된 비트코인 ​​포크로 작성되었으며 그 해 Elements Alpha라는 테스트 네트워크로 출시되었습니다. 이 네트워크는 Taproot는 물론이고 Segwit보다 선행했습니다. 발행 자산보다 선행했으며, 궁극적으로 불필요하게 복잡하고 버그가 발생하기 쉬운 것으로 판명된 2단계 페그 방식으로 Liquid에 배포하기 전에 재설계되었습니다. 주요 기능은 단순화되어 비트코인에 Segwit으로 포팅된 증인 분리를 위한 초기 방식으로 이후 발행된 자산을 지원하도록 확장된 기밀 트랜잭션(Confidential Transactions)과 약정을 가능하게 하기 위한 추가 opcode 세트입니다. 이번 포스팅에서는 후자를 중점적으로 다루겠습니다.

원래 Elements와 Liquid opcode 세트를 도입했을 때 비트코인 스크립트는 수동의 로우레벨 프로그래밍에 의존했으며 공식적으로 분석하기도 어려웠습니다. 우리 확장판은 언어의 표현력을 증가시켰지만 이러한 근본적인 한계는 해결하지 못했습니다. 사용자는 Lending on Liquid와 같은 일부 애플리케이션을 개발할 수 있었지만, 이 개발은 스크립팅 플랫폼에서 프로그래밍하기 쉽다기보다는 인간의 독창성을 입증한 것에 더 가까웠습니다. Bitmatrix와 같은 다른 애플리케이션은 이 포스팅에서 설명한 스크립트 시스템으로 업그레이드한 후에만 배포할 수 있습니다. 이후, Blockstream Research는 Elements와 별개로 이러한 분석 문제를 해결할 비트코인 스크립트를 모델링하는 새로운 방법인 Miniscript를 개발했습니다.

특히 Miniscript를 사용하면 스크립트의 모든 키를 자동으로 열거하고 트랜잭션을 생성하는 데 필요한 집합을 결정할 수 있습니다. 서명을 수집하기 전에 해당 트랜잭션의 크기에 대한 상한선을 찾고, 코인을 사용하기 위해 어떤 조건이 충족되어야 하는지에 대한 의미론적 질문에 답하며, 스크립트와 증인에 대한 정확한 인코딩을 계산합니다. Miniscript는 Blockstream Research의 Andrew Chow와 트랜잭션을 생성하는 데 필요한 데이터를 수집하기 위한 프로토콜을 제공하는 원래 컨셉과 개발에 참여한 Pieter Wuille가 함께 개발한 부분 서명된 비트코인 트랜잭션을 보완합니다. 이를 통해 지갑은 충족되는 정확한 스크립트를 이해(또는 선행)하지 못하더라도 트랜잭션에 서명할 수 있습니다. 단지 트랜잭션이 이해가 되는지 확인하고 ECDSA 또는 Schnorr 서명을 생성한 후, 나머지는 Miniscript 툴이 처리하도록 하기만 하면 됩니다.

그러나 Miniscript를 사용하더라도 Elements에서 ‘쉬운 약정’으로 가는 지름길을 찾지는 못했습니다. 우리의 2016년 약정 구조는 일반적인 방식으로 사용하기 어려웠고, 실제로 큰 크기로 인해 우리가 비트코인에서 상속된 201개의 opcode 제한에 의한 제약을 피하기 어려웠습니다. 따라서 비트코인 스크립트가 Miniscript, PSBT, Tapscript(Miniscript를 고려해 설계됨)를 통해 더욱 강력해졌음에도 불구하고, Elements의 스크립트 확장 기능은 사용할 수 없었습니다.

혁신: 새로운 추상화

2021년 2월, Blockstream 연구원인 Andrew Poelstra와 Sanket Kanjalkar는 마침내 Elements의 약정을 Miniscript와 연계하는 방법을 찾았습니다. 본질적으로, 우리는 모든 트랜잭션의 데이터를 추출하기 위해 CHECKSIGFROMSTACK(CSFS) opcode를 사용하는 고정 크기 스크립트 템플릿인 ‘머신’ 내부에 약정 가능 스크립트를 구성하고 고정된 위치의 스택에 남겨두면 ‘실제 코드’가 효율적으로 액세스할 수 있습니다. 이를 통해 지불 트랜잭션에 대한 많은 조건에서 CSFS 형식 약정의 높은 비용을 상쇄할 수 있습니다.

사실 우리는 나중에 이 초기 접근법에서 진보하게 됐지만, 이 단계를 통해 일반 약정을 실제로 구축하는 방법에 대한 개념 제약을 넘어섰습니다. Elements 스크립트의 실제 제한 사항이 무엇인지 살펴보겠습니다.

  • 비트코인에서 상속된 31비트 빅엔디안 연산은 트랜잭션에 사용되는 64비트 리틀엔디안 양으로 쉽게 사용할 수 없었습니다.
  • 이에 더해, 곱셈 또는 나눗셈 opcode가 없습니다. Elements에는 비트 시프트와 기타 연산이 추가되었지만 수수료 계산 등의 작업을 수행하는 데 필요한 기본 사항은 없었습니다.
  • 수수료 계산의 경우 CSFS형 약정은 트랜잭션 가중치에 액세스할 수 없으므로 수수료를 계산할 수 없습니다.
  • 비트코인의 520바이트 스택 요소 크기 제한과 유연성 없는 SHA256 opcode의 조합으로 인해 520바이트 이상의 데이터를 해시할 수 없습니다. 많은 양의 트랜잭션 데이터를 한 번에 해시해야 하는 CSFS의 필요성에 더해 총 트랜잭션 크기에 제한이 생겨 지갑 생성자의 삶을 훨씬 더 어렵게 합니다.
  • 201개 이상의 opcode가 있는 스크립트를 실행할 수 없으며, CSFS 머신에 수십 개 opcode가 필요하고 64비트 정수를 32비트 정수로 또는 그 반대로 변환하는 데 수십 개가 더 필요합니다.
  • 서명 유효성 검사를 제외하고 타원 곡선 연산을 수행할 수 없으며, 여러 작업을 강제로 수행하게 할 수 있지만 Taproot 출력이 올바르게 형성되었는지 확인하지 못합니다. Tapscript에서 이러한 제한을 유지했다면 스크립트의 복잡성을 증가시키면서 기능을 감소시켰을 것입니다.

이러한 문제를 인식하고 나면 다음과 같은 문제를 쉽게 해결할 수 있습니다.

  • 곱셈을 포함하여 64비트 값을 조작하기 위한 새로운 opcode와, 일반 비트코인 opcode와 함께 사용하기 위해 예전 32비트 값으로 변환하기 위한 opcode를 추가했습니다.
  • 트랜잭션 데이터에 액세스하기 위해 ‘direct introspection’ opcode를 추가하여 ‘the machine’의 필요성을 제거하고 opcode 갯수를 줄였습니다. 또한 수수료 계산을 위해 트랜잭션 가중치에 액세스하기 위해 TXWEIGHT opcode를 추가했습니다.
  • ‘스트리밍 해시’ opcode를 추가하여 모든 데이터를 단일 스택 요소에 넣을 필요 없이 해시 엔진에 데이터를 공급할 수 있습니다. 이렇게 하면 스크립트 인터프리터에서 더 이상 리소스를 사용할 필요 없이 520바이트 스택 요소 제한을 완전히 피할 수 있습니다.
  • Taproot 출력, pay-to-contract 약정, 기타 타원 곡선 암호화 작업을 처리하기 위해 ECMULSCALARVERIFY opcode를 추가했습니다.
  • 위의 개선 사항으로도 관리할 수 있었겠지만 우리는 201개 opcode 제한을 없애기 위해 비트코인의 리드를 따랐습니다.

위와 같은 개선이 완료된 이후, 비트코인 ​​스크립트 모델(예, 도입된 디스크 액세스 없음, 무제한 리소스 사용 또는 루핑과 같은 복잡한 제어 흐름)에 잘 맞는 소규모의 새 opcode 세트를 가지고 Elements에서 프로덕션 등급 약정을 허용하는 큰 문제를 해결했습니다.

다음은 새로운 opcode의 전체 목록입니다.

이 장벽을 극복한 후에, 우리는 Elements에만 해당되는 것이 아닌 약정과 관련된 몇 가지 더 심층적인 문제를 해결할 준비가 됐습니다.

이러한 opcode로 수행할 수 있는 새로운 작업의 구체적인 예로, 60블록 시간 내에서 한 번에 MAX_WITHDRAW만 출금할 수 있도록 제한하는 보관 방식을 고려할 수 있습니다.

이 약정을 생성하기 위해서는 다음과 같은 조건을 준수해야 합니다(Miniscript 표기법에 설명되어 있으며 향후 포스팅에서 자세히 다룰 예정입니다).

  • 부분 출금(partial_withdraw): 약정에 MAX_WITHDRAW 이상이 있으면 약정에 최소 total_value - MAX_WITHDRAW가 남아 있어야 합니다.
  • "num64_gt(curr_inp_v,MAX_WITHDRAW)": 총 입력 sats가 MAX_SATS보다 큽니다.
  • "asset_eq(curr_inp_asset,out_asset(0))": 입력 자산과 출력 자산 0이 동일합니다.
  • "num64_geq(out_v(0),sub64(curr_inp_v,MAX_WITHDRAW))": 출력 값은 최소한 total_value - MAX_WITHDRAW여야 합니다.
  • "spk_eq(curr_inp_spk,out_spk(0))": 남은 코인을 약정으로 보냅니다.
  • 전체 출금(full_withdraw): 약정이 MAX_WITHDRAW sats보다 작으면 출력에 제약이 없습니다.
  • num64_leq(curr_inp_v,MAX_WITHDRAW)
  • 키 컨트롤(keys): 다시 지불하기 전에 60블록을 기다렸다가 K 키가 필요합니다.
  • pk(K): K키의 서명이 필요합니다.
  • older(60): 다음 출금까지 60블록 대기 시간
  • curr_idx_eq(0): 현재 지불 지수가 0이어야 합니다. 이를 통해 우리는 두 개의 약정 utxos를 함께 보낼 수 있고 그 중 하나가 약정을 벗어날 수 있는 소위 ‘반 지불’ 문제를 방지합니다.
  • 마지막으로 위의 모든 조건(keys,or(full_withdraw,partial_withdraw))을 결합합니다.

긴급 출금 키, 다단계 출금, 화이트리스트, 다중서명 수탁을 통해 이를 확장할 수 있습니다.

Open Problems and Future Work 열린 문제와 향후 작업

약정 지원으로 Miniscript를 확장하고 이를 효율적으로 수행하기 위한 opcode를 제공함으로써, 우리는 사용자가 쉽게 약정 스크립트를 구성하고 이러한 스크립트의 동작을 이해하며 약정 계약을 실행하는 완전한 트랜잭션을 생성할 수 있는 프레임워크를 만들었습니다. 그러나 그렇게 함으로써 우리는 Miniscript의 재귀적 구조를 약화시키고 따라서 일부 형태의 분석을 깨뜨립니다.

예를 들어, 두 자식이 두 개의 원본 스크립트인 and_b 노드를 생성하여 두 개의 Miniscript를 ‘구성’하는 것이 가능합니다. (Script의 경우, 원본 스크립트를 연결하고 끝에 BOOLAND opcode를 추가합니다.) 사용자가 각 원본 스크립트를 개별적으로 충족할 수 있다고 확신한다면, 결합된 스크립트를 충족할 수 있다고 확신할 수 있습니다.

반대로, 복잡한 스크립트의 충족 가능성을 확인하려면 스크립트를 재귀적으로 실행할 수 있습니다. AND 노드를 볼 때마다 두 하위 스크립트를 모두 충족해야 하고, OR 노드를 볼 때마다 둘 중 하나를 충족해야 합니다.

약정의 경우, 이러한 형태의 구성은 더 이상 유효하지 않습니다. and_b를 사용하여 두 스크립트를 결합하여 구문적으로 유효한 스크립트를 얻을 수 있지만, 두 하위 스크립트를 모두 만족하더라도 결과가 만족스럽지 않을 수 있습니다. 예를 들어, 자산 A를 태우기 위해 트랜잭션의 첫 번째 출력이 필요한 스크립트와 자산 B를 태우기 위해 트랜잭션의 첫 번째 출력이 필요한 스크립트를 결합하는 것을 고려하십시오. 분명히 둘 다 동시에 충족될 수 없습니다.

일반적으로 약정은 구축 중인 트랜잭션에 글로벌 조건을 도입하기 때문에 개별 스크립트 단편은 다른 스크립트 단편에 비로컬 영향을 미칠 수 있습니다. 또는 Bitcoin Miniscript에서 스크립트를 지불 조건의 단조 함수로 생각할 수 있음을 볼 수 있습니다. 스크립트는 어떤 세트의 지불 조건(서명, 해시 프리이미지 등)이 코인을 지불하기에 유효한지, 유효하지 않은지를 결정하기 위한 규칙을 나타냅니다. 단조로운 이러한 규칙은 일부 조건 세트가 유효하면 모든 상위 세트가 유효함을 의미합니다.

그림의 약정의 경우, 단조 함수 측면에서 이 모델은 단순히 적용되지 않으므로 Miniscript가 제공하는 강력한 범용 분석 도구를 잃게 됩니다. 대신에 약정이 활성화된 Elements Miniscript 프로그램은 특정 질문을 하고 답변을 받는 ad-hoc 방식으로 분석해야 합니다.

현재 우리가 동시에 작업하고 있는 Script를 완전히 대체하는 Simplicity에도 이러한 제한이 있습니다. 이러한 종류의 ad-hoc 분석을 가능하게 하기 위해 Simplicity의 인터프리터는 Coq 정리 증명 도우미에 참조 구현이 있습니다. 다시 말하면, 인간이 적절한 질문을 제시해야 하지만, 그에 대한 대답은 최소한 견고하고 기계로 확인할 수 있다는 뜻입니다. Elements Miniscript를 사용하면 프로그램이 전적으로 사람의 코드 검토에 의존합니다. 그럼에도 불구하고, 우리는 강력한 프로그램이 간단하면서도 매우 정확할 수 있도록 설계하고자 했습니다.

앞으로도 Elements Miniscript의 기능을 계속 확장하고, 이를 통해 약정을 생성하고 상호 작용할 수 있는 지원 도구와 지갑 라이브러리를 작성하고, 새로 확장된 Elements보다 훨씬 더 표현력이 풍부한 Simplicity를 위해 계속 노력할 것입니다.

해피 해킹!

If you have specific preferences, please, mark the topic(s) you would like to read: