Mastering Bitcoin
7. Transacciones » Scripts de Transacción y Lenguaje de Script » Verificación Sin Estado
Página 46 de 98
Scripts de Transacción y Lenguaje de Script
Los clientes bitcoin validan transacciones ejecutando un script escrito en un lenguaje de scripting similar a Forth. Tanto el script de bloqueo (obstrucción) colocado sobre una UTXO como el script de desbloqueo que generalmente contiene una firma son escritos en este lenguaje de scripting. Cuando una transacción es validada, el script de desbloqueo en cada entrada es ejecutado junto con su correspondiente script de bloqueo para verificar que satisfaga la condición de gasto.
Hoy en día la mayoría de las transacciones procesadas a través de la red bitcoin tienen la forma de «Alice paga a Bob» y se basan en el mismo script llamado script de Pago-a-Hash-de-Clave-Pública (script Pay-to-Public-Key-Hash). Sin embargo, el uso de scripts para bloquear outputs y desbloquear inputs significa que mediante el uso del lenguaje de programación las transacciones pueden contener un número infinito de condiciones. Las transacciones bitcoin no se limitan a la forma y patrón de «Alicia paga a Bob».
Esto es tan solo la punta del iceberg de posibilidades que pueden ser expresadas con este lenguaje de scripting. En esta sección haremos una demostración de los componentes del lenguaje de scripting de transacciones bitcoin y mostraremos cómo puede ser utilizado para expresar condiciones complejas para gastar y cómo esas condiciones pueden ser satisfechas por scripts de desbloqueo (unlocking scripts).
La validación de transacciones bitcoin no se basa en un patrón estático, sino que es alcanzada a través de la ejecución de un lenguaje de scripting. Este lenguaje permite una variedad casi infinita de condiciones a ser expresadas. Así es cómo bitcoin adquiere el poder de «dinero programable».
Construcción de Scripts (Bloqueo + Desbloqueo)
El motor de validación de transacciones de bitcoin depende de dos tipos de scripts para validar transacciones: un script de bloqueo (locking script) y un script de desbloqueo (unlocking script).
Un script de bloqueo (locking script) es una obstrucción colocada sobre una salida, el cual especifica las condiciones que deben cumplirse para gastar dicha salida en el futuro. Históricamente a los scripts de bloqueo se los llamaba un scriptPubKey, ya que usualmente contenían una clave pública o dirección bitcoin. En este libro nos referiremos a ellos como «script de bloqueo» para reconocer el mucho mayor espectro de posibilidades de esta tecnología de scripting. En la mayoría de las aplicaciones bitcoin a lo que nos referimos como script de bloqueo aparecerá en el código fuente como scriptPubKey.
Un script de desbloqueo (unlocking script) es un script que «resuelve» o satisface, las condiciones establecidas por una salida y un script de bloqueo y permite que la salida sea gastada. Los scripts de desbloqueo son parte de cada entrada de transacción, y la mayoría de las veces contienen una firma digital producida por la cartera del usuario a partir de su clave privada. Históricamente el script de desbloqueo era llamado scriptSig, ya que usualmente contenía una firma digital. En la mayoría de las aplicaciones bitcoin el código fuente se refiere al script de desbloqueo como scriptSig. En este libro nos referiremos a ellos como «script de desbloqueo» para reconocer el espectro mucho más amplio de requerimientos de scripts de bloqueo, ya que no todos los scripts de desbloqueo requieren firmas.
Todo cliente bitcoin debe validar transacciones ejecutando los scripts de bloqueo y desbloqueo en simultáneo. Para cada entrada de la transacción el software traerá primero la UTXO referenciada por la entrada. Esa UTXO contiene un script de bloqueo definiendo las condiciones requeridas para enviarla. El software de validación luego tomará el script de desbloqueo contenido en la entrada que está intentando gastar esta UTXO y ejecutará ambos scripts.
En el cliente bitcoin original, los scripts de bloqueo y desbloqueo eran concatenados y ejecutados en secuencia. Por razones de seguridad esto fue cambiado en 2010, debido a una vulnerabilidad que permitía que un script de desbloqueo mal formado enviara datos a la pila y corrompiera el script de bloqueo. En la implementación actual los scripts son ejecutados en forma separada y la pila es transferida entre ejecuciones, como se describe a continuación.
Primero, el script de desbloqueo es ejecutado utilizando el motor de ejecución de pila. Si el script de desbloqueo es ejecutado sin errores (por ejemplo, no posee operadores sobrantes «colgando»), la pila principal (no la pila alternativa) es copiada y el script de bloqueo es ejecutado. Si el resultado de ejecutar el script de bloqueo con los datos de la pila copiados del script de desbloqueo es «VERDADERO», el script de desbloqueo ha sido exitoso en resolver las condiciones impuestas por el script de bloqueo y, por tanto, la entrada es una autorización válida para gastar la UTXO. Si cualquier resultado que no sea «VERDADERO» permanece luego de la ejecución del script combinado, la entrada es inválida porque ha fallado en satisfacer las condiciones de gastado colocadas sobre la UTXO. Nótese que la UTXO es registrada permanentemente en la cadena de bloques, y por ello es invariable y no se ve afectada por intentos fallidos de gastarla por referencia en una nueva transacción. Únicamente una nueva transacción que satisface las condiciones de la UTXO correctamente resulta en la UTXO siendo marcada como «gastada» y removida de la reserva de UTXOs disponibles (sin gastar). Combinando scriptSig y scriptPubKey para evaluar un script de transacción es un ejemplo de los scripts de desbloqueo y bloqueo para el tipo más común de transacción bitcoin (un pago a un hash de clave pública), mostrando el script combinado que resulta de la concatenación de los scripts de desbloqueo y bloqueo previo a la validación por script.

Figura 1. Combinando scriptSig y scriptPubKey para evaluar un script de transacción
Lenguaje de Scripting
El lenguaje de scripts de transacción bitcoin, llamado Script, es un lenguaje de ejecución basada en pila con notación polaca inversa similar a Forth. Si eso no tiene sentido para ti, probablemente sea que no has estudiado lenguajes de programación de la década de 1960. Script es un lenguaje muy simple diseñado para ser limitado en alcance y ejecutable en un rango amplio de hardware, quizá hasta tan simple como un dispositivo embebido, tal como una calculadora de mano. Requiere procesamiento mínimo y no puede hacer muchas de las cosas sofisticadas que los lenguajes modernos sí pueden. En el caso del dinero programable, esto es una medida intencional de seguridad.
El lenguaje de scripting de bitcoin es llamado un lenguaje de ejecución basado en pila porque utiliza una estructura de datos llamada una pila (stack). Una pila es una estructura de datos muy simple, la cual puede ser visualizada como una pila de cartas. Una pila permite realizar dos operaciones: empujar y sacar. Empujar añade un elemento al tope de la pila. Sacar remueve el elemento en el tope de la pila.
El lenguaje de scripting ejecuta el script procesando cada ítem de izquierda a derecha. Los números (constantes de datos) son empujados a la pila. Los operadores empujan o sacan uno o más parámetros de la pila, actúan sobre ellos, y pueden empujar un resultado a la pila. Por ejemplo, OP_ADD sacará dos elementos de la pila, los sumará, y luego empujará la suma resultante a la pila.
Los operadores condicionales evalúan una condición, produciendo un resultado booleano de VERDADERO o FALSO. Por ejemplo, OP_EQUAL saca dos elementos de la pila y empuja VERDADERO (donde VERDADERO es representado por el número 1) si son iguales y FALSO (representado por cero) si no son iguales. Los scripts de transacción bitcoin usualmente contienen un operador condicional, de forma que puedan producir el valor VERDADERO que significa que la transacción es válida. En el script de validación de Bitcoin haciendo matemática simple, el script:
2 3 OP_ADD 5 OP_EQUAL
Muestra el operador de adición aritmética OP_ADD, el cual suma dos números y coloca el resultado en la pila, seguido por el operador condicional OP_EQUAL, el cual verifica que el resultado de la suma sea igual a 5. Para ser concisos, el prefijo OP_ es omitido en el ejemplo paso-a-paso.
Lo que sigue es un script levemente más complejo, el cual calcula 2 + 7 - 3 + 1. Nótese que cuando el script contiene varios operadores en hilera, la pila permite que los resultados de un operador sean utilizados por el siguiente operador:
2 7 OP_ADD 3 OP_SUB 1 OP_ADD 7 OP_EQUAL
Intenta validar el script previo tú mismo usando papel y lápiz. Cuando la ejecución del script acaba, deberías terminar con el valor VERDADERO en la pila.
Aunque la mayoría de los scripts de bloqueo se refieren a una dirección bitcoin o clave pública, y por lo tanto requiriendo prueba de pertenencia para gastar los fondos, el script no necesita ser tan complicado. Una combinación de scripts de bloqueo y desbloqueo que resulta en VERDADERO es válido. La aritmética simple que usamos como ejemplo del lenguaje de scripting es también un script de bloqueo válido que puede ser usado para bloquear una salida de transacción.
Usar parte del script de ejemplo aritmético como el script de bloqueo:
3 OP_ADD 5 OP_EQUAL
Lo cual puede ser satisfecho por una transacción que contenga una entrada con el script de desbloqueo:
2
El software de validación combina los scripts de bloqueo y desbloqueo y el script resultante es:
2 3 OP_ADD 5 OP_EQUAL
Como vimos en el ejemplo paso-a-paso en El script de validación de Bitcoin haciendo matemática simple, cuando el script es ejecutado, el resultado es OP_TRUE, haciendo a la transacción válida. No solo es esto un script de bloqueo de salida de transacción válido, sino que el UTXO resultante puede ser gastado por cualquiera con la habilidad aritmética para saber que el número 2 satisface el script.

Figura 2. El script de validación de Bitcoin haciendo matemática simple
Las transacciones son válidas si el resultado en el tope de la pila es VERDADERO, cualquier valor distinto de cero o si la pila se encuentra vacía luego de la ejecución del script. Las transacciones son inválidas si el valor en el tope de la pila es FALSO (un valor vacío de longitud cero), o si la ejecución del script es detenida explícitamente por un operador, tal como OP_VERIFY, OP_RETURN, o un condicional terminante como OP_ENDIF. Ver operadores usados para manipular la pila para más detalles.
Incompletitud Turing
El lenguaje de script de transacciones bitcoin contiene muchos operadores, pero se encuentra deliberadamente limitado en una forma importante. No tiene la capacidad de realizar bucles ni controles de flujo complejos más allá de los controles de flujo condicionales. Esto asegura que el lenguaje no es Turing Completo, lo cual significa que los scripts tienen complejidad limitada y tiempos de ejecución predecibles. Script no es un lenguaje de propósito general. Estas limitaciones aseguran que el lenguaje no pueda ser usado para crear un bucle infinito u otras formas de «bombas lógicas» que pudieran ser embebidas en una transacción de forma que causara un ataque de denegación de servicio contra la red bitcoin. Recuerda, cada transacción es validada por cada nodo completo en la red bitcoin. Un lenguaje limitado previene que el mecanismo de validación de transacciones sea usado como una vulnerabilidad.
Verificación Sin Estado
El lenguaje de script de transacciones bitcoin es carente de estado en el sentido en que no existe un estado previo a la ejecución del script, o un estado guardado luego de la ejecución del script. Por lo tanto, toda la información necesaria para ejecutar el script se encuentra contenida en el mismo script.
Un script se ejecutará con bastante predecibilidad de la misma forma en cualquier sistema. Si tu sistema verifica un script, puedes estar seguro que cualquier otro sistema en la red bitcoin también verificará el script, lo cual significa que una transacción es válida para todos y todos saben esto. Esta predictibilidad de resultados es un beneficio esencial del sistema bitcoin.