A
Bitcoin a veces se lo denomina
dinero programable. Debido a su naturaleza digital, confiere a los usuarios una gran flexibilidad cuando hay que establecer condiciones respecto a cómo pueden gastarse los fondos.
Al analizar Bitcoin, hablamos de
monederos y
monedas. Pero también podríamos pensar en los monederos como claves, las monedas como cheques, y la
blockchain como una fila tras otra de cajas fuertes cerradas. Cada caja fuerte presenta una fina ranura, a través de la cual, todo el mundo puede depositar cheques o comprobar cuánto valor hay dentro. Sin embargo, sólo el dueño de la llave podrá acceder a la misma.
Cuando el dueño de una clave desea entregar dinero a otra persona, desbloquea su caja. Prepara un nuevo cheque que hace referencia al anterior (el cual, a continuación, es destruido) y lo deja bloqueado en una caja que el receptor puede abrir. Para gastarlo, el nuevo receptor repetirá el proceso.
En este artículo, analizaremos más detenidamente el Script, el lenguaje de programación que los
nodos de la red Bitcoin interpretan. El Script es lo que gobierna el mecanismo de bloqueo/desbloqueo mencionado en relación con las cajas fuertes.
Siguiendo con nuestra analogía anterior, podríamos decir que en cada transacción hay dos partes: una clave (para desbloquear tu caja) y una cerradura. Utilizarás tu clave para abrir la caja que contiene el cheque que deseas enviar, y a continuación, incluirás uno nuevo en otra caja con una cerradura distinta. Para poder gastar de la nueva caja, necesitarás otra clave.
Es muy simple. Podrás disponer también de ciertas variedades en cuanto a tipos de cerraduras del sistema. Quizás algunas cajas fuertes requerirán que aportes
múltiples claves, mientras otras te exigirán que demuestres el conocimiento de un secreto. Existen un montón de condiciones que la gente puede establecer.
Nuestra clave es lo que denominamos un scriptSig. La cerradura es nuestro scriptPubKey. Si nos fijamos en ambos componentes con mayor detalle, nos daremos cuenta que en realidad están formados por bits de datos y bloques de código. Cuando se combinan, crean un pequeño programa.
Cuando realizas una transacción, estás transmitiendo dicha combinación a la red. Cada nodo que la recibe comprobará el programa, que le revelará si la transacción es válida. De lo contrario, simplemente será descartada, y no podrás gastar los fondos bloqueados.
Los cheques (monedas) de que dispones se denominan
unspent transaction outputs (outputs de transacciones no gastadas o UTXOs). Los fondos pueden ser utilizados por cualquiera que pueda proporcionar la llave que encaja en la cerradura. Específicamente, la clave es el scriptSig, mientras que la cerradura es el scriptPubKey.
Si los UTXOs se encuentran en tu monedero, probablemente presenten una condición que diga
sólo la persona que pueda demostrar la propiedad de esta clave pública podrá desbloquear estos fondos. Para desbloquearlo, proporcionarás un scriptSig que incluya una
firma digital, utilizando la
clave privada que se compara con la clave pública especificada en el scriptPubKey. Todo esto quedará más claro a continuación.
El Script es lo que se conoce como un lenguaje basado en un "stack". Lo que significa es que, cuando leemos un conjunto de instrucciones, las colocamos en lo que puede considerarse una especie de columna vertical. La lista A, B, C, por ejemplo, daría como resultado un "stack" en el que A se hallaría en el fondo y C en la cima. Cuando las instrucciones nos dicen que hagamos algo, operaremos con uno o múltiples elementos empezando por la cima del stack.
Los elementos A, B y C añadidos y "expelidos" del stack.
Podemos distinguir entre los datos (cosas como las
firmas,
hashes y claves públicas) y las instrucciones (u
opcodes). Las instrucciones eliminan los datos y hacen algo con ellos. Aquí tenemos un ejemplo muy simple del aspecto que podría tener un script:
<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <check if equal>
En rojo, tenemos los datos, y en azul, los opcodes. Leemos de izquierda a derecha, por lo que primero colocamos la secuencia <xyz> en el stack. Luego le sigue el opcode <md5 hasher>. Éste no existe en Bitcoin, pero digamos que elimina el elemento superior del stack (<xyz>) y lo somete a hash mediante el algoritmo MD5. A continuación, el output es añadido nuevamente al stack. El output en este caso resulta ser d16fb36f0911f878998c136191af705e.
¡Qué coincidencia! Nuestro próximo elemento a añadir es <d16fb36f0911f878998c136191af705e>, por lo que nuestro stack tendrá ahora dos elementos idénticos. Finalmente, <check if equal> expele dos elementos de la parte superior y comprueba si son iguales. Si lo son, añadirá <1> al stack. De lo contrario, añadirá <0>.
Hemos llegado al final de nuestra lista de instrucciones. Nuestro script podría haber fallado de dos maneras – si el elemento que quedara finalmente fuera un cero, o si uno de los operadores provocara el fallo cuando algunas de las condiciones no se cumplieran. No teníamos ningún operador de ese tipo en este ejemplo, y hemos acabado con un elemento distinto a cero (<1>), por lo que nuestro script era válido. Estas reglas también son válidas para las transacciones de Bitcoin auténticas.
El anterior era un programa inventado. Veamos ahora algunos reales.
Pay-to-Pubkey (P2PK) es sumamente sencillo. Consiste en bloquear fondos para una clave pública concreta. Si quisieras recibir fondos de esta forma, deberías proporcionarle al emisor tu clave pública, en lugar de tu dirección de Bitcoin.
La
primera transacción entre
Satoshi Nakamoto y Hal Finney en 2009 sería de tipo P2PK. Dicha estructura sería intensamente utilizada durante los primeros días de Bitcoin, pero hoy en día, Pay-to-Pubkey-Hash (P2PKH) la ha reemplazado en gran medida.
El script de bloqueo de una transacción P2PK sigue el formato <public key> OP_CHECKSIG. Muy sencillo. Es posible que hayas deducido que OP_CHECKSIG coteja una firma con la clave pública proporcionada. De esta forma, nuestro scriptSig será una simple <signature>. Recuerda, el scriptSig es la llave de la cerradura.
No podría ser más simple. Una firma se añade al stack, seguida por una clave pública. OP_CHECKSIG las expele y coteja la firma contra la clave pública. Si concuerdan, se añade un <1> al stack. De lo contrario, se añade un <0>.
Por motivos que desarrollaremos en la siguiente sección, P2PK en realidad ya no se utiliza más.
Pay-to-Pubkey-Hash (P2PKH) es en la actualidad el tipo de transacción más común. A no ser que hagas el esfuerzo de descargar software arcaico, tu monedero ejecutará estas transacciones por defecto.
El scriptPubKey en P2PKH es el siguiente:
OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG
Antes de presentar el scriptSig, procedemos a desglosar qué harán los nuevos opcodes:
OP_DUP
OP_DUP expele el primer elemento y lo duplica. A continuación, añade de nuevo ambos elementos en el stack. Normalmente, esto se hace para poder ejecutar una operación en el duplicado sin afectar al original.
OP_HASH160
Éste expele el primer elemento y lo somete a hash dos veces. La primera ronda de hashing usa el algoritmo SHA-256. El output de SHA-256 es a continuación sometido a hash mediante el algoritmo RIPEMD-160. El output resultante se añade de nuevo al stack.
OP_EQUALVERIFY
OP_EQUALVERIFY combina otros dos operadores –OP_EQUAL y OP_VERIFY. El primero expele dos elementos y comprueba si son idénticos. De ser así, añade un 1 al stack. De lo contrario, añade un 0. OP_VERIFY, por su parte, expele el elemento superior y comprueba si es Cierto (es decir, distinto a 0). Si no lo es, la transacción falla. Combinados, OP_EQUALVERIFY provoca que la transacción falle si los dos elementos superiores no concuerdan.
Esta vez, el scriptSig tendrá este aspecto:
<signature> <public key>
Deberás proporcionar una firma y la correspondiente clave pública para desbloquear outputs de P2PKH.
Puedes observar lo que ocurre en el GIF de arriba. No es muy distinto de un script P2PK. Simplemente estamos añadiendo un paso extra para comprobar que la clave pública concuerda con el hash del script.
Hay algo a tener en cuenta, sin embargo. En un script de bloqueo P2PKH, la clave pública no es visible, solo podemos ver su hash. Si vamos a un
explorador de blockchain y observamos una salida P2PKH que no se ha gastado, no podemos determinar la clave pública. Solo se revela cuando el receptor decide transferir los fondos.
Esto tiene un par de beneficios. La primera es que el hash de clave pública es simplemente más fácil de pasar que una clave pública completa. Satoshi lo lanzó en 2009 por esta misma razón. El hash de clave pública es lo que hoy conocemos como una dirección de Bitcoin.
El segundo beneficio es que los hashes de clave pública podrían proporcionar una capa adicional de seguridad contra la
computación cuántica. Debido a que nuestra clave pública no se conoce hasta que gastamos los fondos, es aún más difícil para otros calcular la clave privada. Tendrían que revertir las dos rondas de hashing (RIPEMD-160 y SHA-256) para obtenerlo.
Pay-to-Script-Hash (P2SH) fue un desarrollo muy interesante para
Bitcoin. Permite que el remitente bloquee los fondos en el hash de un script; no necesitan saber qué hace realmente el script. Toma el siguiente hash SHA-256:
e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1
No necesitas saber la entrada del hash para bloquear los fondos. Sin embargo, el gastador debe proporcionar el script que se utilizó para hacer el hash y debe satisfacer las condiciones de ese script.
El hash anterior se creó a partir del siguiente script:
< multiplicar por 2> <4> < verificar si es igual>
Si deseas gastar las monedas vinculadas a ese scriptPubKey, no solo proporcionas esos comandos. También necesitas un scriptSig que haga que el script completado se evalúe como True. En este ejemplo, ese es un elemento que para obtener un resultado de <4>. Por supuesto, eso significa que nuestro scriptSig es solo <2>.
En la vida real, el scriptPubKey para una salida P2SH es:
OP_HASH160 < redeemScript hash> OP_EQUAL
No hay nuevos operadores aquí. Pero, tenemos < redeemScript hash> como un nuevo elemento. Como su nombre lo indica, es un hash del script que debemos proporcionar para canjear los fondos (llamado redeemScript). El scriptSig cambiará según lo que se encuentre en el redeemScript. En general, sin embargo, descubrirás que es una combinación de firmas y las claves públicas adjuntas, seguida del redeemScript (obligatorio).
< firma> < clave pública> < redeemScript>
Nuestra evaluación difiere un poco de la ejecución de stack que hemos visto hasta ahora. Sucede en dos partes. El primero simplemente verifica que hayas proporcionado el hash correcto.
Notarás que no hacemos nada con los elementos que preceden al redeemScript. No se usan en este momento. Hemos llegado al final de este mini programa, y el elemento superior no es cero. Eso significa que es válido.
Sin embargo, aún no hemos terminado. Los
nodos de red reconocen esta estructura como P2SH, por lo que en realidad tienen los elementos de scriptSig esperando en otro stack. Ahí es donde se usarán la firma y la clave pública.
Hasta ahora, hemos tratado el redeemScript como un elemento. Pero ahora, se interpretará como instrucciones, que podrían ser cualquier cosa. Tomemos el ejemplo de un script de bloqueo P2PKH, al que debemos proporcionar la < firma> y la < clave pública> que coincida con un < hash de clave pública> dentro del < redeemScript>.
Una vez que se ha expandido redeemScript, puedes ver que tenemos una situación que se ve exactamente como una transacción P2PKH normal. A partir de ahí, simplemente lo ejecutas como lo harías con uno normal.
Aquí hemos demostrado lo que se llama un script P2SH (P2PKH), pero es poco probable que encuentre uno de esos en la naturaleza. Nada te impide hacer uno, pero no te brinda beneficios adicionales y terminas ocupando más espacio en un bloque (y, por lo tanto, cuesta más).
P2SH generalmente es útil para cosas como
Multisignature o las transacciones compatibles con
SegWit. Las transacciones multisig pueden ser de gran tamaño, ya que pueden requerir múltiples claves. Antes de la implementación de Pay-to-Script-Hash, un remitente tendría que enumerar todas las claves públicas posibles en su script de bloqueo.
Pero con P2SH, no importa cuán complejas sean las condiciones de gasto. El hash de redeemScript siempre tiene un tamaño fijo. Por lo tanto, los costos se transfieren a los usuarios que desean desbloquear el script de bloqueo.
La compatibilidad con SegWit es otro caso en el que P2SH es útil (en la siguiente sección veremos los detalles de cómo difiere la estructura de la transacción). SegWit fue una bifurcación suave que resultó en un cambio a los formatos de bloque / transacción. Debido a que es una actualización opcional, no todos los softwares de carteras reconocen los cambios.
Eso no importa si los clientes envuelven el hash de script SegWit en P2SH. Al igual que con todas las transacciones de este tipo, no necesitan saber cuál será el redeem script de desbloqueo .
Para comprender el formato de transacción en SegWit, solo necesitas saber que ya no solo tenemos un scriptSig y un scriptPubKey. Ahora, también tenemos un nuevo campo llamado testigo. Los datos que solíamos guardar en el scriptSig se mueven al testigo, por lo que el scriptSig está vacío.
Si has encontrado direcciones que comienzan con "bc1", eso es lo que llamamos nativo de SegWit (en lugar de solo compatible con SegWit, que comienza con un "3" ya que son direcciones P2SH).
Pay-to-Witness-Pubkey-Hash (P2WPKH)
Pay-to-Witness-Pubkey-Hash (P2WPKH) es la versión SegWit de P2PKH. Nuestro testigo se ve así:
< firma> < clave pública>
Notarás que esto es lo mismo que el scriptSig de P2PKH. Aquí, el scriptSig está vacío. Mientras tanto, el scriptPubKey se parece a lo siguiente:
< OP_0> < hash de clave pública>
Eso se ve un poco extraño, ¿no? ¿Dónde están los códigos de operación para permitirnos comparar la firma, la clave pública y su hash?
No mostramos operadores adicionales aquí, porque los nodos que reciben la transacción saben qué hacer con ella en función de la longitud del < hash de clave pública>. Calcularán la longitud y comprenderán que debe ejecutarse en el mismo estilo que una buena transacción P2PKH.
Los nodos no actualizados no saben cómo interpretar la transacción de esa manera, pero no importa. Según las viejas reglas, no hay testigos, por lo que leen un scriptSig vacío y algunos datos. Evalúan esto y lo marcan como válido: en lo que a ellos respecta, cualquiera podría gastar la salida. Es por eso que SegWit se considera una
bifurcación suave compatible con versiones anteriores.
Pay-to-Witness-Script-Hash (P2WSH)
Pay-to-Witness-Script Hash (P2WSH) es el nuevo P2SH. Si has llegado hasta aquí, probablemente puedas averiguar cómo se verá esto, pero lo revisaremos de todos modos. Nuestro testigo es lo que normalmente pondríamos en el scriptSig. En un P2WSH que envuelve una transacción P2PKH, por ejemplo, podría verse así:
< firma 1> < clave pública>
Aquí está nuestro scriptPubKey:
< OP_0> < hash de script>
Las mismas reglas se mantienen. Los nodos SegWit leen la longitud del hash del script y determinan que es una salida P2WSH, que se evalúa de manera similar a P2SH. Mientras tanto, los nodos antiguos solo lo ven como una salida que cualquiera puede gastar.
En este artículo, hemos aprendido un poco sobre los componentes básicos de Bitcoin. Resumamos rápidamente:
Tipo de Script | Descripción |
---|
Pay-to-Pubkey (P2PK) | Bloquea fondos a una clave pública particular |
Pay-to-Pubkey-Hash (P2PKH) | Bloquea fondos a un hash de clave pública particular (es decir, una dirección) |
Pay-to-Script-Hash (P2SH) | Bloquea fondos en el hash de un script que el destinatario puede proporcionar |
Pay-to-Witness-Pubkey-Hash (P2WPKH) | La versión SegWit de P2PK |
Pay-to-Witness-Script-Hash (P2WSH) | La versión SegWit de P2SH |
Una vez que profundizas en Bitcoin, comienzas a comprender por qué tiene tanto potencial. Las transacciones pueden estar formadas por muchos componentes diferentes. Al manipular estos componentes básicos, los usuarios tienen una gran flexibilidad cuando se trata de establecer condiciones sobre cómo y cuándo se pueden gastar los fondos.