Saltar al contenido

Output Descriptors

Tiempo de lectura aprox: 8 minutos, 17 segundos

Este artículo tiene como objetivo introducir y explicar de manera general el concepto de los descriptores de salida (output descriptors), que se están empezando a utilizar en las carteras de Bitcoin.

Es importante mencionar que es un concepto relativamente nuevo en el ecosistema de Bitcoin. Trataremos de explicar de manera general su concepto, sin embargo las implementaciones podrían variar dependiendo del software que lo realice, así como también es posible que aún continué evolucionando en el tiempo.

En la documentación en inglés este termino lo encontraras o bien como wallet descriptor o bien como output descriptors, en general se refieren a lo mismo.

Si tu nivel de conocimiento sobre Bitcoin es avanzado, muy probablemente te interese ir directamente aquí, donde hablo sobre lo que significan los descriptores de salida.

Antecedentes

Antes de entrar en materia vamos a tomar un poco de perspectiva y ubicarnos, recordando que el principal objetivo de Bitcoin es permitir el intercambio de valor entre pares.

Dicho de otra forma, el objetivo de Bitcoin es facilitar que el dueño legitimo de un bitcoin pueda gastarlo, y para ello el protocolo debe asegurarse que puede hacerlo (validación de llaves).

Además, al ser Bitcoin un medio de intercambio electrónico, permite un sin fin de posibilidades en cuanto a como condicionar el gasto (ejecución de scripts).

Es importante entender que las carteras juegan un papel fundamental en cuanto a la gestión de las condiciones de gasto.

Antes de continuar repasemos algunos conceptos básicos.

1) Bitcoin Script

Desde que Bitcoin fue creado por Satoshi, contiene un lenguaje de programación y permite la creación de pequeños contratos inteligentes.

Ese lenguaje está basado en pilas (stack), y fue diseñado para implementar de manera muy especifica la forma en que un Bitcoin puede ser gastado.

Esas formas de programar el gasto pueden ser variadas:

  • Por firmas,
  • Por bloqueos por hash,
  • Por bloqueos de tiempo (nlockTime),
  • etc.

Con ello puedes realizar combinaciones de condiciones para poder gastar un bitcoin donde el límite son los casos de uso que se nos ocurran:

  • Podrías implementar una condición donde no se pueda realizar el gasto, aunque se tengan las firmas, si antes no se cumple con un tiempo determinado,
  • Un gasto que solo pueda ejecutarse si no se excede con el 20% del balance,
  • etc.

Pero todo tiene un momento. Cuando Bitcoin fue creado, si bien estas posibilidades existían, no se tenían todas las herramientas para explotarlas.

2) Miniscripting

En agosto de 2019, Pieter Wuille liberó una versión de una implementación que ayudaría a Bitcoin a mejorar su programabilidad: el Bitcoin Miniscripting.

Miniscripting es un lenguaje que permite poder escribir y ejecutar de manera estructurada una secuencia de programas simples de Bitcoin (Bitcoin Scripts), para facilitar el análisis de lo escrito y su interpretación.

Esto que se resume en los dos párrafos anteriores es de una potencia inimaginable. Con el uso de miniscripts se puede programar cualquier secuencia de gasto. El límite serán los casos de uso, no la programación en si.

Para decirlo claramente, con esto se podría programar cualquier smart contract que implicara el gasto de bitcoin y las condiciones para validarlo.

3) Carteras

Legacy

Las legacy, que es como se suelen llamar al tipo que el propio Satoshi Nakamoto desarrolló, son muy básicas.

No derivan desde una semilla las claves, sino que por cada dirección generan una clave privada diferente.

Su diseño esta basado en un modelo orientado a la gestión de llaves. Es una colección no estructurada de llaves, scripts y metadatos, que tiene a groso modo una lógica ad-hoc, es decir, ajustada al manejo exacto de las firmas. Esto le resta potencia y versatilidad a las carteras a la hora de querer gestionar diferentes condiciones de gasto. Limita la programación de la cartera.

Hierarchical Deterministic

Las carteras HD dieron un gran salto permitiendo derivar a partir de una única semilla (las palabras), infinitas claves privadas y públicas de una cartera. También trajeron consigo la implementación de nuevos scripts.

Ejemplos de ello es la posibilidad de conjuntar en una transacción multifirma, una llave de tipo segwit, con una legacy.

Sin embargo aunque más versátiles, siguen teniendo limitaciones. Por ejemplo, hoy día si creas una cartera multifirma, estableces de inicio la forma en que el gasto va a poder realizarse (2 firmas de 3, por ejemplo) pero no hay posibilidad que dentro de esa misma cartera se programe una transacción que permita otra forma de gasto. Ni tampoco indicar de manera estandarizada cuales son todos los componentes que se necesitan tener para poder recuperarla de manera segura.

Pieter Wuille realizó un primer análisis de esta problemática cuando se estaba planteando la introducción de Segwit en Bitcoin, un nuevo tipo de script que permitiría a Bitcoin evolucionar en cuanto a la posibilidad de gasto, como iremos viendo. Te dejo el link al documento de análisis por si quieres revisarlo, pero como comenté al inicio no entraremos en los detalles más técnicos.

Documento de Pieter Wuille: https://gist.github.com/sipa/125cfa1615946d0c3f3eec2ad7f250a2

A todo lo anterior le faltaba todavía algo. Una pieza que permitiera indicar de manera unificada a cualquier cartera, como una clave privada había sido creada, las N posibilidades de gasto que se pudieran tener y el poder indicarle cuales son los componentes que se necesitan tener para poder recuperarla.

Esa pieza, son los descriptores de salida u output descriptors.

Wallet Descriptor

Entonces, recapitulemos un poco para no perdernos, ¿qué tenemos actualmente?

  • La arquitectura actual, funciona en base a un modelo centrado en llaves, scripts y metadatos que lo que hace es tomar una llave y a partir de esa llave obtener direcciones y scriptPubKeys.
  • Este modelo ocasiona que una llave contenga multiples direcciones pero esas direcciones tienen predeterminado su comportamiento, no pueden volverse creativos.
  • Si hablamos de scripts, con este modelo no se pueden generar direcciones que a su vez contengan scripts que hagan cosas distintas en función del tipo de gasto que queramos definir.
  • Y además, carteras más complejas requieren mayor número de elementos a considerar para el respaldo y la restauración de sus fondos. Sin mecanismo que permitan obtenerlos de manera automatizada y sencilla, los esfuerzos de programación a día de hoy son grandes.

Output descriptors

El objetivo de Bitcoin es facilitar el intercambio de valor de manera programática, así que conociendo las limitaciones que el modelo actual tiene, podemos entender por qué la comunidad esta planteando el desarrollo de un nuevo modelo.

Un nuevo modelo que además busca crear un estándar para facilitar el desarrollo de las carteras.

Aquí entran en juego los descriptores de salida.

¿Qué son los descriptores?

En programación un descriptor es una cadena legible para el ser humano que describe datos.

En el caso de las carteras, el descriptor es un guión (como el de una película) que contiene todas las instrucciones necesarias para recuperar la parte pública o privada de la cartera. Y un conjunto de pequeños programas que podrán ser ejecutados por las carteras para entender las condiciones de gasto de bitcoin.

Los descriptores de salida, proporcionan explícitamente una dirección con instrucciones programadas de como podrá gastarse, así como todas las claves y scripts que se necesitan para firmarlos (a esto se le llama guión).

Esto significa que si se tienen las claves privadas relacionadas a ese guión, se puede producir el script de desbloqueo que satisface las condiciones de gasto.

El descriptor de salida permite tener en un código de tipo script toda esta información:

  • las claves que se usan (ya sean privadas y/o públicas)
  • las claves publicas necesarias
  • el tipo de dirección y el camino de derivación usado, para cada clave publica
  • el script usado en la dirección (multisig, segwit, etc)

Este modelo de output descriptors ha trabajado en realizar una abstracción para separar las claves y así independizar la forma en que se producen los scripts de salida de una transacción de Bitcoin (scriptPubKey).

Con esta separación lo que se consigue es no tener que suponer cosas para generar un script de salida. Sino que lo sabemos con certeza.

Se pasa a un modelo donde se tienen scripts dentro de un gestor que los almacena y solo cuando se necesitan firmar, se le “pregunta” a ese gestor de scripts qué hace falta para firmarlo y es este gestor el encargado de investigar, basándose en los descriptores, lo que necesita para ello.

Veamos un ejemplo:

Vamos a suponer que tenemos una cartera donde generamos una dirección que tenga la siguiente política de gasto:

  • Multi-firma.
  • Tipo de multifirma pay-to-witness-script-hash (indicando que se necesitan al menos 2 firmas de 3 para gastar).
  • Que el pago no se ejecutará hasta pasados 10 días de su propagación en la red.

Con las anteriores condiciones, tendríamos un descriptor como este ejemplo:

sh(wsh(and_v(vc:pk_h(tprv8ZgxMBicQKsPePmENhT9N9yiSfTtDoC1f39P7nNmgEyCB6Nm4Qiv1muq4CykB9jtnQg2VitBrWh8PJU8LHzoGMHTrS2VKBSgAz7Ssjf9S3P, 03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a/0/*),older(1440))))

Con ese descriptor, una cartera generaría una dirección como la siguiente que ya lleva intrínsecas las instrucciones anteriores:

2Mtk2nyS98MCi2P7TkoBGLaJviBy956XxB1

De ahí la potencia de los descriptores. Al generarse la dirección, las instrucciones ya están programadas en la misma, por lo cual el gasto solo se realizará cuando se cumplan.

Adicionalmente ayudan a los usuarios a abstraerse de la gestión del backup de la cartera. Lo que quiere decir que el output descriptor no solo proporciona las instrucciones de gasto (miniscripts), sino que ayuda con el respaldo.

Constituyen un gran paso adelante para hacer que las carteras sean más portables a través de diferentes herramientas y aplicaciones.

Por primera vez crean un lenguaje común para describir instrucciones de bitcoin que los desarrolladores pueden utilizar e integrar en su software. Ya sean carteras de software, de hardware y últimamente en aplicaciones para el control de Bitcoin en custodia.

Otro ejemplo para entender más fácilmente su potencia, lo podemos ver en el código QR que genera specter desktop para el backup de una cartera 2 de 3 con segwit native.

Como ya hemos comentado, todo el detalle de las características de la cartera esta contenido en los descriptores. Los descriptores son instrucciones y esas instrucciones pueden traducirse en un código QR, el cual a su vez puede ser leído por otra cartera, como fully noded e importar, de una sola lectura todas las características sin que el usuario tenga que preocuparse de conocer las claves publicas necesarias, el tipo de dirección usado, el camino de derivación, el tipo de script usado, etc.

En el ejemplo podemos ver como toda la información de las firmas (publicas en este caso), dan como resultado descriptores de salida y toda esa información es accesible mediante un simple código QR. Esto es solo un ejemplo de todo lo que podría conseguirse con los descriptores.

Esto supone un avance impresionante, una cartera con descriptores permitirá soportar cualquier tipo de descriptor, existente o nuevo.

El uso de los descriptores puedes generar algunas dudas, cómo por ejemplo:
  • ¿Qué pasa si quiero mover mi semilla de una cartera sin descriptores a una que usa descriptores?
    • La respuesta es que no pasa nada si mueves a una nueva cartera que si usa descriptores desde una que no los usa, porque simplemente en la nueva no recibirá ningún descriptor.
    • Para que los descriptores pasen entre dos carteras, la cartera que generó las direcciones necesita tener y usar la implementación de descriptores y la cartera a la que se transfieren las direcciones necesita reconocer esos descriptores.
  • Y una vez que tengo una cartera que usa descriptores, ¿tengo que guardarlos de alguna forma?
    • Si, la información de los descriptores son todos los metadatos adicionales y se han de almacenar para permitir a las carteras hacer uso de esa información y poder recuperar el tipo de dirección, el camino de derivación, el tipo de script usado, etc., a la hora de recuperar fondos.
¿Y qué beneficios nos traen?
  • Agrupar datos en un formato estandarizado, proporcionando detalles importantes de configuración de una cartera.
  • Permiten que las carteras sean fácilmente expandibles hacía nuevos scripts de gasto, basta con cambiar o insertar nuevos descriptores y la cartera podrá interpretarlos y generar nuevas direcciones de ese tipo (un ejemplo es la propuesta de Taproot que introduce un nuevo tipo de dirección y guiones de salida). Las carteras que usen descriptores podrán actualizarse para utilizar nuevos tipos de direcciones con mucha más facilidad.
  • El monedero en sí mismo no necesita saber cómo firmar, por ejemplo una nueva multisig, el descriptor dirá cómo firmarlo.
  • Otra cosa importante es que los descriptores son completamente inequívocos en cuanto a las rutas de derivación que se están usando y qué tipo de dirección se están produciendo. Esto significa que si quieres importar un descriptor a otro monedero no tienes que adivinar qué ruta de derivación y qué tipo de dirección utilizar. El descriptor le indica exactamente a la cartera lo que debe producir. Esto es sumamente importante, ya que facilita las copias de seguridad.
    • Nota: ¿Conoces el sitio walletsrecovery.org?, pues con la estandarización y el uso generalizado en las carteras de los descriptores, podríamos olvidarnos de consultarlo!
  • Por consecuencia la interoperabilidad entre carteras será ahora más fácil de implementar
  • También ayudan para que se implemente el soporte de Hardware Wallets de manera más simple y potente.

¿Te imaginas poder empezar a tener carteras que permitan incluir de manera “sencilla” funcionalidades que no solo te permitan interactuar con bitcoin sino también, con lightning network o con diferentes cadenas, sin tener que guardar un montón de datos?. Pues esto será posible con los descriptores de salida, con solo una semilla los descriptores podrán encargarse de identificar toda la información que se necesita guardar.

Imagen ejemplo de una propuesta de un nuevo derivation path tomado del github del proyecto RGB

Conclusión:

Gracias a la posibilidad de programación del gasto a través de miniscripts y la facilidad de almacenaje y restauración que los descriptores vienen a proveer, se podrán llegar a programar smart contracts mucho más complejos, además de unificar el «lenguaje» en que se programan.

También, utilizando los descriptores, la actualización de esta nueva generación de carteras será mucho más simple e inmediata, pudiendo adaptarnos con facilidad a nuevas condiciones de gastos (nuevos scripts) para Bitcoin.

Un gran salto sin lugar a dudas para escalar la forma de programar el gasto dentro de Bitcoin. Los descriptores son la pieza que faltaba y que facilita el almacenaje y la restauración de carteras con scripts muy complejos.

Como siempre agradezco que hayas llegado hasta aquí.

Si te ha gustado este artículo, te ha resultado de utilidad o tienes algún comentario, por favor déjamelo dejando aquí un comentario o a través de de twitter en mi cuenta: @decentralized_b. También puedes dejarme una propina, te lo agradeceré mucho y me motiva a seguir generando contenido ya que con ello podré tener más tiempo para hacerlo.

Pero lo más importante, si crees que puede ayudar a otros más, compártelo.

Sobre el autor de este artículo