비트코인에 「쓰지 못하게 꺼 둔 명령어」가 있다는 이야기는 꽤 알려져 있어요. 그런데 그게 정확히 몇 개인지, 지금도 그런지, 그리고 최신 스크립트 방식에서도 같은지는 한 줄로 정리된 곳이 잘 없어요.
먼저 답부터 적을게요. 지금 비트코인 코어 소스에서 합의 규칙 층에 꺼져 있는 명령어는 15개예요. 그리고 이 15개는 탭스크립트에서 한 개도 빠짐없이 만나는 즉시 검증 성공으로 처리돼요. 같은 바이트 값이 한쪽에서는 거래를 통째로 무효로 만들고, 다른 쪽에서는 그 자리에서 성공 판정을 내려요.
이 글이 재는 자리를 못박을게요. 비트코인 코어 v31.1 소스 한 벌을 커밋 하나로 고정해 내려받은 뒤, 명령어 열거표와 꺼진 명령어 조건문과 탭스크립트 성공 판정 함수를 각각 통째로 떼어 읽었어요. 대상 커밋은 9be056a8a72b로 시작하고 커밋 시각은 2026년 7월 6일 오후 10시 9분(한국 시각)이에요. 조회 시각은 2026년 9월 12일 오후 1시 9분부터 1시 15분까지(한국 시각) 이고, 노드를 빌드하거나 실행하지는 않았어요. 매매 권유가 아니에요.
![]()
받은 소스가 정말 그 커밋인지부터 대조했어요
숫자를 말하기 전에 재료부터 확인했어요. 인터넷에서 받은 압축본을 그냥 믿고 세면, 세는 방법이 옳아도 결과가 틀릴 수 있으니까요.
그래서 내려받은 파일 네 개의 git blob 해시를 직접 계산한 다음, 같은 커밋을 가리키도록 저장소 API에 물어봐 돌려받은 값과 한 글자씩 맞춰 봤어요.
| 파일 | blob 해시 (로컬 = 원격) | 결과 |
|---|---|---|
src/script/script.h | b06be9c975d3b4... | 일치 |
src/script/script.cpp | 829e351be697a1... | 일치 |
src/script/interpreter.cpp | 443714ceedaf6b... | 일치 |
src/policy/policy.h | 0bc7b327708332... | 일치 |
네 개 전부 일치했어요. 그러니까 아래 숫자들은 「받은 걸 믿고 셌다」가 아니라 대조해서 확인한 뒤 셌다에 해당해요.
명령어가 몇 개인지부터 분모를 정했어요
「비트코인 스크립트 명령어는 몇 개」라는 질문은 세는 법을 정하지 않으면 답이 세 개로 갈려요.
| 세는 기준 | 값 |
|---|---|
| 소스 열거표에 적힌 이름 개수 | 117개 |
| 그 이름들이 가리키는 서로 다른 바이트 값 | 113개 |
| 한 바이트로 표현할 수 있는 전체 자리 | 256개 |
| 이름이 하나도 없는 빈 바이트 값 | 143개 |
| 그중 길이를 적어 데이터를 밀어넣는 자리 | 75개 |
| 그중 밀어넣기도 아닌 진짜 빈자리 | 68개 |
손검산도 맞아요. 113 더하기 75 더하기 68이 정확히 256이에요.
이름 117개와 값 113개가 어긋나는 이유는 같은 값에 이름이 두 개 붙은 자리가 4쌍이기 때문이에요.
| 바이트 값 | 붙어 있는 이름 | 사연 |
|---|---|---|
| 0번 | OP_0, OP_FALSE | 같은 것에 붙인 별명 |
| 81번 | OP_1, OP_TRUE | 같은 것에 붙인 별명 |
| 177번 | OP_CHECKLOCKTIMEVERIFY, OP_NOP2 | 빈자리가 기능을 받으며 이름이 늘어난 자리 |
| 178번 | OP_CHECKSEQUENCEVERIFY, OP_NOP3 | 같은 경우 |
그래서 117을 명령어 개수라고 말하면 틀려요. 값 기준으로는 113개이고, 그 113개 안에도 실제 명령이 아니라 표식으로만 쓰이는 항목이 섞여 있어요. 뒤쪽 두 쌍은 옛날에 비어 있던 자리가 나중에 기능을 받으면서 이름이 하나 더 붙은 경우라, 목록이 고정된 것이 아니라는 증거이기도 해요.
투자 정보 안내
본 글은 암호화폐 시장에 대한 객관 데이터·정보 제공 목적이며, 특정 코인 매수·매도 권유가 아닙니다. 암호화폐는 변동성이 매우 크고 원금 전액 손실이 가능하므로 본인 판단과 책임 하에 결정하세요.
꺼진 명령어는 정확히 15개예요
해석기 소스에는 꺼진 명령어를 걸러 내는 조건문이 딱 하나 있어요. 열여섯 줄짜리 조건문이고, 바로 위에 2010년 취약점 번호가 주석으로 붙어 있어요. 거기에 이름이 적힌 것이 전부예요.
| 바이트 값 | 이름 | 원래 하려던 일 |
|---|---|---|
| 126번 | OP_CAT | 두 조각 이어 붙이기 |
| 127번 | OP_SUBSTR | 가운데 잘라내기 |
| 128번 | OP_LEFT | 앞에서 자르기 |
| 129번 | OP_RIGHT | 뒤에서 자르기 |
| 131번 | OP_INVERT | 비트 뒤집기 |
| 132번 | OP_AND | 비트 곱 |
| 133번 | OP_OR | 비트 합 |
| 134번 | OP_XOR | 비트 배타합 |
| 141번 | OP_2MUL | 두 배 |
| 142번 | OP_2DIV | 반으로 나누기 |
| 149번 | OP_MUL | 곱하기 |
| 150번 | OP_DIV | 나누기 |
| 151번 | OP_MOD | 나머지 |
| 152번 | OP_LSHIFT | 왼쪽으로 밀기 |
| 153번 | OP_RSHIFT | 오른쪽으로 밀기 |
문자열을 다루는 넷, 비트 연산 넷, 곱셈과 나눗셈 계열 다섯, 자리 밀기 둘이에요. 이어 붙이기 하나만 살아 있었어도 지금 스크립트로 할 수 있는 일이 꽤 달라졌을 거예요.
실행되지 않는 갈래에 적어 둬도 스크립트가 죽어요
여기가 이 15개의 진짜 성질이에요. 보통 프로그램이라면 조건이 거짓이라 건너뛰는 자리에 뭐가 적혀 있든 상관이 없죠. 비트코인 스크립트는 달라요.
해석기가 명령어 하나를 처리하는 순서를 소스 줄 번호로 확인했어요.
| 순서 | 하는 일 |
|---|---|
| 첫째 | 명령어 한 개를 읽어요 |
| 둘째 | 밀어넣는 값이 너무 크지 않은지 봐요 |
| 셋째 | 명령어 개수 제한을 세요 |
| 넷째 | 꺼진 명령어인지 검사하고, 맞으면 여기서 바로 실패해요 |
| 다섯째 | 지금이 실행되는 갈래인지 판정하고 본체로 들어가요 |
넷째가 다섯째보다 앞이에요. 두 자리는 소스에서 스무 줄 남짓 떨어져 있는데, 그 순서 때문에 꺼진 명령어는 「어차피 안 도는 자리」에 적어 둬도 그냥 실패해요. 조건문 안쪽에 숨겨 두는 우회가 통하지 않는다는 뜻이에요.
여기에 같은 성질을 가진 명령어가 두 개 더 붙어요. OP_VERIF와 OP_VERNOTIF예요. 이 둘이 합류하는 경로는 조금 달라요. 다섯째 단계의 조건이 「실행되는 갈래이거나, 또는 조건문 관련 바이트 범위에 드는 명령어」인데, 그 범위가 99번부터 104번까지라 101번과 102번이 그 안에 들어가요. 그런데 이 둘은 본체에 담당 항목이 아예 없어서 마지막 기본 항목으로 떨어지고, 「모르는 명령어」 오류가 돼요.
| 바이트 값 | 이름 | 본체에 담당 항목이 있나 |
|---|---|---|
| 99번 | OP_IF | 있어요 |
| 100번 | OP_NOTIF | 있어요 |
| 101번 | OP_VERIF | 없어요, 무조건 실패 |
| 102번 | OP_VERNOTIF | 없어요, 무조건 실패 |
| 103번 | OP_ELSE | 있어요 |
| 104번 | OP_ENDIF | 있어요 |
정리하면 실행되지 않는 자리에 있어도 스크립트를 죽이는 명령어는 17개예요. 꺼진 15개에 이 둘을 더한 값이에요.
그런데 탭스크립트에서는 그 15개가 전부 성공 처리돼요
여기서 이야기가 뒤집혀요.
타프루트가 들어오면서 생긴 새 스크립트 방식인 탭스크립트에는 「무조건 성공」이라는 규칙이 있어요. 정해진 바이트 값을 만나면 뒤를 더 보지 않고 그 스크립트의 검증을 성공으로 끝내요. 앞으로 새 기능을 넣을 자리를 미리 비워 두려고 만든 장치예요.
그 값이 몇 개인지 세어 봤어요. 87개였어요.
87은 손으로도 검산했어요. 80번과 98번이 하나씩, 126번부터 129번까지 넷, 131번부터 134번까지 넷, 137번과 138번 둘, 141번과 142번 둘, 149번부터 153번까지 다섯, 그리고 187번부터 254번까지 예순여덟이에요. 1 더하기 1 더하기 4 더하기 4 더하기 2 더하기 2 더하기 5 더하기 68이 87이에요.
내역을 갈라 보면 이래요.
| 구분 | 개수 |
|---|---|
| 이름이 붙어 있는 값 | 19개 |
| 이름이 아예 없는 빈자리 | 68개 |
| 합계 | 87개 |
이름이 붙어 있는 19개는 꺼진 명령어 15개에 OP_RESERVED, OP_VER, OP_RESERVED1, OP_RESERVED2 네 개를 더한 것이에요. 그러니까 두 목록을 교집합하면 이렇게 돼요.
| 대조 | 결과 |
|---|---|
| 꺼진 15개 중 탭스크립트에서 무조건 성공이 되는 것 | 15개 전부 |
OP_VERIF와 OP_VERNOTIF가 무조건 성공이 되는 것 | 하나도 없음 |
같은 바이트 값인데 정반대 결과예요. 레거시 스크립트에서는 조건문 안에 숨겨 둬도 거래를 통째로 무효로 만드는 15개가, 탭스크립트에서는 만나는 즉시 검증 성공으로 뒤집혀요. 그리고 끝까지 치명적인 것은 OP_VERIF와 OP_VERNOTIF 둘뿐이에요.
명세 원문과 구현 코드가 숫자 목록까지 같았어요
숫자 하나를 소스에서만 세면, 세는 코드가 틀렸을 때 알아차릴 방법이 없어요. 그래서 저장소가 다른 원본 두 개를 맞대 봤어요.
한쪽은 비트코인 코어의 판정 함수예요. 세 줄짜리 반환식 하나가 전부예요.
opcode == 80 || opcode == 98 || (opcode >= 126 && opcode <= 129) ||
(opcode >= 131 && opcode <= 134) || (opcode >= 137 && opcode <= 138) ||
(opcode >= 141 && opcode <= 142) || (opcode >= 149 && opcode <= 153) ||
(opcode >= 187 && opcode <= 254)
다른 한쪽은 탭스크립트를 규정한 BIP-342 원문이에요. 이 문서는 다른 저장소에 있고, 해당 파일에 마지막으로 손댄 커밋으로 고정해 받았어요. 본문의 검증 절차 둘째 단계에 숫자 목록이 그대로 적혀 있어요. 80번, 98번, 126번부터 129번까지, 131번부터 134번까지, 137번과 138번, 141번과 142번, 149번부터 153번까지, 187번부터 254번까지를 만나면 검증이 성공한다고요. 그리고 그 값들을 통틀어 부르는 이름도 문서가 직접 정해 두고 있어요.
두 목록은 숫자 하나까지 완전히 같았어요. 관리 주체가 다른 저장소 두 곳에서 각각 확인한 값이 일치했고, 거기에 손으로 한 덧셈까지 같은 값을 냈어요. 세 방향이 맞은 셈이에요.

그래도 도둑질 구멍이 아닌 이유
「검증이 무조건 성공한다」는 문장만 떼어 놓고 보면 아무나 남의 돈을 가져갈 수 있는 것처럼 읽혀요. 그렇지 않아요.
탭스크립트는 쓰는 쪽이 미리 출력에 묶어 둔 스크립트를 나중에 공개해서 쓰는 구조예요. 제3자가 나중에 아무 바이트나 끼워 넣을 수 있는 자리가 아니에요. 그러니 무조건 성공 바이트가 들어간 스크립트를 만들어 두면, 손해를 보는 쪽은 그 출력을 만든 본인이에요. BIP-342도 각주에서 같은 이유를 들면서, 아주 초기 비트코인에서는 그런 바이트를 서명 자리에 끼워 넣을 수 있어서 실제로 문제가 됐던 구조와 대비해 두고 있어요.
타프루트 출력이 지갑에서 어떤 경로로 만들어지는지, 같은 시드 문구에서 왜 주소가 여러 개 나오는지는 지갑 파생 경로 네 갈래에 따로 정리해 뒀어요. 그쪽 글에서 86번 경로가 바로 이 글에서 말하는 타프루트 출력으로 이어지는 자리예요.
투자 정보 안내
본 글은 암호화폐 시장에 대한 객관 데이터·정보 제공 목적이며, 특정 코인 매수·매도 권유가 아닙니다. 암호화폐는 변동성이 매우 크고 원금 전액 손실이 가능하므로 본인 판단과 책임 하에 결정하세요.
탭스크립트에서만 되는 것과 탭스크립트에서만 막히는 것
방향이 한쪽만 있는 건 아니에요. 같은 소스에서 반대 방향도 확인했어요.
| 항목 | 개수 | 내용 |
|---|---|---|
| 탭스크립트에서만 쓸 수 있는 명령어 | 1개 | OP_CHECKSIGADD 하나예요. 레거시와 세그윗 초기 방식에서는 그 자리에서 바로 「모르는 명령어」로 실패해요 |
| 탭스크립트에서만 막히는 명령어 | 2개 | OP_CHECKMULTISIG와 OP_CHECKMULTISIGVERIFY예요. 레거시에서는 멀쩡히 도는데 탭스크립트에서는 전용 오류로 끝나요 |
| 아직 비어 있는 재활용 대기 명령어 | 8개 | 이름은 있는데 하는 일이 없는 자리예요 |
앞에서 본 별명 4쌍 때문에 헷갈리기 쉬운 자리가 여기예요. OP_NOP2와 OP_NOP3은 이름만 보면 비어 있는 것 같지만 이미 기능을 받은 자리라 이 8개에 들어가지 않아요.
명령어 개수 제한도 방식마다 달라요. 스크립트 하나에 명령어를 201개까지만 쓸 수 있다는 제한이 있는데, 이 제한은 레거시와 세그윗 초기 방식에만 적용돼요. 세는 코드 자체가 스크립트 방식을 따지는 조건 안에 들어 있어서, 탭스크립트에서는 아예 세지 않아요. 참고로 그 201이라는 값도 모든 명령어를 세는 게 아니라 96번보다 큰 명령어만 세요.

「꺼졌다」는 말에는 층이 두 개예요
이 주제에서 가장 자주 섞이는 자리라 따로 적을게요.
꺼진 15개는 합의 규칙 층 이야기예요. 규칙을 지키는 노드라면 전부 같은 판정을 내려요. 반면 무조건 성공 바이트가 들어간 거래가 네트워크에 잘 안 퍼지는 건 중계 정책 층 이야기예요. 코어의 기본 중계 정책 묶음에는 무조건 성공 명령어를 권장하지 않는다는 표시가 들어 있어서, 그런 거래는 합의상 유효하더라도 표준 거래로는 취급되지 않아요.
그래서 「막힌다」는 말을 들었을 때 그게 규칙 층인지 정책 층인지를 먼저 구분해야 해요. 이 두 층이 어긋나서 생기는 혼란은 스크립트 말고도 여러 곳에 있어요. 거래를 되돌리는 규칙에서 명세 원문과 실제 구현 정책이 어떻게 갈리는지는 거래 대체 규칙 대조 쪽에 정리해 뒀어요.
이 숫자가 흔들리는 자리를 적어 둘게요
같은 자리를 다시 세려는 분을 위해 함정부터 적을게요.
첫째, 87이라는 숫자가 이 소스에 두 번 등장하는데 서로 아무 관계가 없어요. 탭스크립트에서 무조건 성공으로 처리되는 바이트 값이 87개이고, 그와 별개로 레거시 해석기 본체의 큰 분기문이 실제로 처리하는 명령어 종류도 87가지예요. 세는 대상도 다르고 근거가 되는 코드도 다른데 값만 같아요. 이 둘을 엮어서 설명하면 없는 인과를 지어내는 것이 돼요. 우연히 같은 값이라고 못박아 둘게요.
둘째, 그 분기문을 세면 88이 나오기 쉬워요. 소스 중간에 주석 처리된 라벨이 한 줄 섞여 있어서, 주석을 먼저 지우지 않고 세면 87 대신 88이 나와요. 87이 맞아요.
셋째, 15와 19를 혼동하기 쉬워요. 무조건 성공 87개 중 이름이 붙은 것은 19개인데, 그중 4개는 꺼진 목록에 없는 별개 항목이에요. 꺼진 명령어는 어디까지나 15개예요.
넷째, 전부 버전에 매인 값이에요. 117도 113도 15도 17도 87도 v31.1의 한 커밋에서 센 값이에요. 다음 소프트포크로 빈자리 하나가 기능을 받으면 무조건 성공 값이 줄고 이름 수가 늘어요. 앞에서 본 177번과 178번 자리가 실제로 그렇게 이름이 늘어난 전례예요. 인용하실 때는 버전과 커밋을 같이 적어 주시는 편이 안전해요. 같은 소스를 놓고 다른 것을 센 이야기는 비트코인 노드 설정 옵션 쪽 글에 있어요. 그쪽은 설정 파일 쪽을 셌고, 이 글은 스크립트 명령어 쪽을 셌어요.
확인하지 못한 것
범위를 분명히 해 둘게요.
- 노드를 빌드하거나 실행하지 않았어요. 소스 텍스트와 명세 문서를 읽고 대조한 것까지가 전부예요. 실제로 꺼진 명령어가 든 거래를 만들어 보내 본 적은 없어요.
- 각 명령어가 원래 무슨 계산을 하려던 것이었는지는 이름과 주석으로만 읽었어요. 옛 구현을 되살려 동작을 확인하지는 않았어요.
- 중계 정책 쪽은 기본 설정 기준이에요. 노드마다 설정을 바꿀 수 있어서, 모든 노드가 같은 거래를 같은 방식으로 취급한다고 말할 수는 없어요.
- 87개 목록이 앞으로 어떻게 쓰일지는 확인할 방법이 없어요. 새 기능을 넣을 자리로 비워 둔 것이라는 설명까지가 문서에 적힌 내용이에요.
자주 묻는 질문 (FAQ)
Q. 꺼진 명령어를 다시 켤 수는 없나요?
규칙을 바꾸는 일이라 소프트포크 같은 절차가 필요해요. 그리고 탭스크립트 쪽에서는 이미 그 자리들이 무조건 성공으로 잡혀 있어서, 그 자리를 쓰려면 무조건 성공 목록에서 빼는 작업이 같이 따라와요. 이 글은 그런 제안이 어디까지 와 있는지는 확인하지 않았어요.
Q. 이어 붙이기 명령어가 왜 그렇게 자주 언급되나요?
조각 두 개를 이어 붙이는 동작이 없으면 스크립트 안에서 만들 수 있는 조건이 크게 줄어들거든요. 그래서 꺼진 15개 중에서도 유독 이 명령어가 이야기에 자주 나와요. 다만 이 글은 그 명령어가 살아났을 때 무엇이 가능해지는지는 검증하지 않았어요.
Q. 무조건 성공 바이트를 넣은 거래를 만들면 어떻게 되나요?
합의 규칙상으로는 유효하지만 기본 정책을 쓰는 노드가 잘 중계하지 않아요. 그리고 그런 스크립트를 자기 출력에 묶어 두면 나중에 아무나 그 조건으로 쓸 수 있게 되는 셈이라, 실제로 그렇게 할 이유가 없어요.
Q. 명령어 이름 117개를 전부 외워야 하나요?
그럴 필요는 없어요. 다만 목록을 볼 때 이름 수와 값 수가 다르다는 것만 알아 두면 좋아요. 어떤 자료는 117을 적고 어떤 자료는 113을 적는데, 둘 다 틀린 게 아니라 세는 기준이 다른 것이에요.
Q. 명령어 개수 제한 201은 어디에 적용되나요?
레거시와 세그윗 초기 방식에만 적용돼요. 탭스크립트는 이 제한을 세지 않아요. 그래서 탭스크립트 쪽 한도는 명령어 개수가 아니라 다른 기준으로 잡혀 있어요.
Q. 다음 버전에서도 이 숫자가 맞나요?
값이 바뀔 수 있어요. 이 글의 숫자는 전부 v31.1의 한 커밋 기준이고, 빈자리가 기능을 받으면 그 즉시 여러 값이 같이 움직여요. 이름이 두 개 붙은 자리가 4쌍이나 있다는 사실 자체가 목록이 계속 바뀌어 왔다는 증거예요.
정리
- 비트코인 코어 v31.1 기준으로 합의 규칙 층에서 꺼져 있는 명령어는 15개예요. 문자열 넷, 비트 연산 넷, 곱셈과 나눗셈 계열 다섯, 자리 밀기 둘이에요.
- 이 15개는 실행되지 않는 갈래에 적어 둬도 스크립트를 통째로 무효로 만들어요. 검사 순서가 갈래 판정보다 앞이기 때문이에요. 같은 성질을 가진 것은
OP_VERIF와OP_VERNOTIF를 더해 모두 17개예요. - 탭스크립트에서 무조건 성공으로 처리되는 바이트 값은 87개이고, 꺼진 15개가 하나도 빠짐없이 그 안에 들어 있어요. 끝까지 치명적인 것은
OP_VERIF와OP_VERNOTIF둘뿐이에요. - 87은 BIP-342 원문의 숫자 목록과 코어 소스의 조건식이 완전히 일치했고, 손으로 한 덧셈까지 같은 값이었어요.
- 탭스크립트에서만 쓸 수 있는 명령어는 1개, 탭스크립트에서만 막히는 명령어는 2개, 명령어 개수 제한 201은 탭스크립트에 적용되지 않아요.
- 모든 숫자는 v31.1, 커밋
9be056a8a72b, 2026년 9월 12일 오후 1시 9분(한국 시각) 조회 기준이고, 노드를 빌드하거나 실행해 확인한 값은 아니에요.
이 글은 매매 권유가 아닌 정보 제공 글입니다. 가상자산은 변동성이 크고 원금 손실 위험이 있으며, 투자 판단과 그 결과에 대한 책임은 본인에게 있습니다.