용어집 / 개발자 참고 자료
OP_VERIFY
- OP_VERIFY란 무엇입니까?
- OP_VERIFY는 스택의 최상단 항목을 꺼내고, 그 항목이 참이 아니면 스크립트 전체를 중단시키며, 이로써 비트코인 스크립트의 모든 검사는 반드시 충족해야 하는 요건이 됩니다.
바이트 0x69는 이 언어의 범용 단언입니다. 스크립트는 OP_IF로 분기할 수 있지만, 오류를 일으킬 방법도 없고 스택에 남는 값 외에 반환값도 없습니다. 그래서 계약은 답을 계산해 이 명령어에 넘기는 방식으로 조건을 요구합니다. 지금까지 이루어진 거의 모든 결제가 이 명령어를 거쳤으며, 대개는 하나로 합쳐진 명령어 안에 숨어 있었습니다.
작동 방식
OP_VERIFY는 스택에서 항목 하나를 제거하고 한 가지 질문만 던집니다. 이것은 참인가?
여기서 참은 숫자가 아니라 바이트 수준의 개념입니다. 빈 항목은 거짓입니다. 0으로만 이루어진 바이트열도 거짓이고, 음의 0, 즉 0x80으로 끝나는 영들의 바이트열도 거짓입니다. 그 밖의 모든 것은 참으로 간주되며, 20바이트 해시나 33바이트 공개 키도 마찬가지입니다. 항목이 거짓이면 스크립트는 그 자리에서 종료되고 그 항목을 담고 있던 입력은 무효가 되며, 이는 그 입력을 둘러싼 트랜잭션을 무효로 만듭니다. 항목이 참이면 스택에 아무것도 다시 올라가지 않고 평가는 다음 명령어로 넘어갑니다.
이 동작 덕분에 스크립트는 여러 가지를 순서대로 검사할 수 있습니다. 스크립트는 마지막에 참인 값이 정확히 하나 남을 때만 성공하므로, 단언이 없다면 모든 조건을 마지막의 불리언 하나로 합쳐야 합니다. 단언이 있으면 계약은 타임록을 요구하고, 이어서 해시 프리이미지를 요구하고, 이어서 서명을 요구할 수 있으며, 처음 조건이 충족되지 않는 순간 실패합니다.
네 개의 명령어는 단언이 내장되어 있으며 오직 1바이트를 아끼기 위해 존재합니다. OP_EQUALVERIFY, OP_CHECKSIGVERIFY, OP_CHECKMULTISIGVERIFY, OP_NUMEQUALVERIFY입니다. 이들이 일반적인 경우를 모두 처리하므로 메인넷에서 단독 0x69는 드뭅니다.
이름에 VERIFY가 들어가면서도 이렇게 작동하지 않는 명령어가 두 개 있습니다. BIP65의 OP_CHECKLOCKTIMEVERIFY와 BIP112의 OP_CHECKSEQUENCEVERIFY는 이전에는 의미가 없던 무동작 명령어 코드 위에 소프트 포크로 추가되었으므로, 구형 노드가 문제없이 작동하도록 하려면 스택을 발견한 그대로 두어야 했습니다. 둘 다 최상단 항목을 검사하고 조건이 충족되지 않으면 실패하지만, 항목을 소모하지는 않습니다. 그래서 실제 스크립트에서는 항상 이 명령어 뒤에 OP_DROP가 따라옵니다.
확인할 수 있는 곳
단언은 스크립트가 증명할 것이 두 가지 이상인 곳에 모여 있습니다.
단순한 타임록 금고는 공개 키, OP_CHECKSIGVERIFY, 144블록(대략 하루) 정도의 지연, 이어서 OP_CHECKSEQUENCEVERIFY 순으로 읽힙니다. 라이트닝 네트워크의 커밋먼트 스크립트와 HTLC 스크립트도 같은 방식으로 만들어지며, 프리이미지 검사와 서명 검사를 연결하여 어느 하나만으로는 자금을 옮길 수 없게 합니다. Taproot의 스크립트 경로 지출은 이 패턴에 크게 의존하는데, tapscript가 OP_CHECKMULTISIG를 OP_CHECKSIGADD로 대체하고 VERIFY 형태를 사용해 다중 키 정책을 간결하게 유지하기 때문입니다.
여러 데스크톱 지갑이 이제 지출 조건을 읽기 쉬운 형태로 기술하는 데 사용하는 정책 컴파일러인 Miniscript는 이러한 명령어를 대신 생성합니다. 원시 스크립트를 직접 작성하지 않고 단언이 생성되는 모습을 볼 수 있는 가장 쉬운 방법입니다.
OP_VERIFY vs OP_RETURN
OP_VERIFY와 OP_RETURN은 둘 다 스크립트를 실패로 끝내지만, 먼저 질문을 던지는 것은 하나뿐입니다. OP_VERIFY는 넘겨받은 값이 거짓일 때 실패하므로, 올바른 지출은 흔적을 남기지 않고 그대로 통과합니다. 바이트 0x6a인 OP_RETURN은 실행될 때마다 실패하며 아무런 조건도 붙지 않습니다. 바로 그렇기 때문에 절대 지출할 수 없는 출력을 만들고 트랜잭션에 데이터를 첨부하는 데 사용됩니다. 첫 번째에서 종료되는 스크립트는 잘못된 것을 넘겨받은 것입니다. 두 번째에서 종료되는 스크립트는 처음부터 지출 가능하도록 만들어진 적이 없습니다.