Inicio Arquitectura técnica Bitcoin Script

Bitcoin Script

El lenguaje de programación de Bitcoin: un sistema basado en pila, minimalista y deliberadamente no Turing-completo, que define las condiciones bajo las cuales un UTXO puede gastarse.

Qué es Bitcoin Script

Bitcoin Script es el lenguaje de programación que utiliza Bitcoin para especificar las condiciones que deben cumplirse para gastar un UTXO. A diferencia de los lenguajes de propósito general, Script está diseñado con un objetivo muy concreto: validar transferencias de valor de forma segura, determinista y verificable por cualquier nodo de la red.

Script fue introducido por Satoshi Nakamoto en el código original. Es un lenguaje basado en pila (stack-based), similar en espíritu a Forth, donde las instrucciones (opcodes) operan sobre valores que se apilan y desapilan durante la ejecución. No tiene variables, ni bucles, ni acceso a estado externo.

Por qué no es Turing-completo (y por qué esto es una característica)

Script no permite bucles ni recursión. Esta limitación es intencional y profundamente importante:

  • Predecibilidad del costo de validación: cualquier nodo puede determinar antes de ejecutar un script cuántos recursos consumirá, evitando ataques de denegación de servicio.
  • Determinismo: el resultado de ejecutar un script es idéntico en todos los nodos del mundo.
  • Auditabilidad: un lenguaje simple es más fácil de razonar, revisar y formalizar. Cuantas menos primitivas, menor la superficie de ataque.
En palabras de la comunidad: "Script hace pocas cosas, pero las hace bien y para siempre".

scriptSig y scriptPubKey

Cada gasto de un UTXO involucra dos scripts:

  • scriptPubKey (locking script): está en el UTXO que se quiere gastar. Define las condiciones para desbloquearlo.
  • scriptSig (unlocking script): lo proporciona quien intenta gastar el UTXO. Contiene firmas, claves públicas o cualquier dato necesario para satisfacer el scriptPubKey.

Se ejecutan secuencialmente: primero el scriptSig deja datos en la pila, luego el scriptPubKey los consume y valida.

Tipos de scripts estándar

TipoDescripciónDirección
P2PKPay-to-PubKey. Formato original; hoy obsoleto.(sin estándar)
P2PKHPay-to-PubKey-Hash. Formato dominante durante años.1...
P2SHPay-to-Script-Hash (BIP-16). Permite multisig y contratos arbitrarios.3...
P2WPKHSegWit v0 nativo: versión compacta de P2PKH.bc1q... (20B)
P2WSHSegWit v0 nativo: versión compacta de P2SH.bc1q... (32B)
P2TRTaproot (BIP-341). Schnorr + MAST. Máxima eficiencia y privacidad.bc1p...

Opcodes clave

  • OP_DUP: duplica el elemento en la cima de la pila.
  • OP_HASH160: aplica SHA-256 seguido de RIPEMD-160.
  • OP_EQUALVERIFY: compara los dos elementos superiores; si difieren, aborta.
  • OP_CHECKSIG: verifica una firma contra una clave pública y la transacción actual.
  • OP_CHECKMULTISIG: verifica N firmas contra M claves (multisig clásico).
  • OP_CHECKLOCKTIMEVERIFY (CLTV, BIP-65): timelock absoluto.
  • OP_CHECKSEQUENCEVERIFY (CSV, BIP-112): timelock relativo.
  • OP_RETURN: marca un output como inservible y permite adjuntar hasta 80 bytes de datos arbitrarios.

Ejemplo: ejecución paso a paso de un P2PKH

scriptPubKey: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

scriptSig aportado por quien gasta: <sig> <pubKey>

  1. Empujar <sig> → pila: [sig]
  2. Empujar <pubKey> → pila: [sig, pubKey]
  3. OP_DUP → pila: [sig, pubKey, pubKey]
  4. OP_HASH160 → pila: [sig, pubKey, HASH160(pubKey)]
  5. Empujar <pubKeyHash> → pila: [sig, pubKey, HASH160(pubKey), pubKeyHash]
  6. OP_EQUALVERIFY: compara los dos superiores. Si no coinciden, aborta → pila: [sig, pubKey]
  7. OP_CHECKSIG: verifica que sig es firma válida de pubKey sobre la transacción → pila: [true]

Al quedar true, el script es válido y el UTXO puede gastarse.

Tapscript (BIP-342)

Con Taproot llegó Tapscript: una versión actualizada del lenguaje que incorpora firmas Schnorr, reemplaza OP_CHECKMULTISIG por OP_CHECKSIGADD (más flexible y barato), elimina límites obsoletos, y reserva espacio para futuros opcodes mediante OP_SUCCESS, facilitando soft-forks posteriores.

Por qué el conservadurismo en Script es una decisión de seguridad

Bitcoin ha añadido muy pocos opcodes en su historia y ha desactivado varios que existían originalmente (OP_CAT, OP_MUL, OP_SUBSTR) tras descubrirse vectores de ataque. Cada opcode que se añade es un compromiso permanente: los errores no pueden revertirse sin un hard fork. Por eso cualquier propuesta atraviesa años de revisión antes de activarse en mainnet.

Errores habituales

  • Creer que Bitcoin Script debería ser Turing-completo: su limitación es una decisión de diseño deliberada, no una carencia.
  • Confundir scriptSig con una firma: el scriptSig puede contener firmas, claves, preimágenes o cualquier dato necesario.
  • Pensar que Taproot reemplaza a Script: Tapscript coexiste con Script y se activa solo cuando se gasta por la rama de script.
  • Asumir que P2SH revela el script al recibir fondos: el redeemScript solo se revela al gastar, no al recibir.
  • Usar OP_RETURN creyendo que los datos quedan fuera de la blockchain: están en cadena, pero el output es inservible como UTXO.

Conceptos relacionados

Fuentes primarias

  • Bitcoin Wiki: Script (en.bitcoin.it/wiki/Script)
  • Andreas Antonopoulos, Mastering Bitcoin (2ª ed.), Capítulo 6
  • BIP-16 (P2SH), BIP-141 (SegWit), BIP-341 (Taproot), BIP-342 (Tapscript)
  • Jimmy Song, Programming Bitcoin, Capítulos 6 y 8