Home < Bitcoin Core Dev Tech < Bitcoin Core Dev Tech 2023 (Apr) < Bitcoin Core Dev Tech 2022 < Bitcoin Core Dev Tech 2019 < Bitcoin Core Dev Tech 2018 (Oct) < Bitcoin Core Dev Tech 2018 (Mar) < Bitcoin Core Dev Tech 2017 < Bitcoin Core Dev Tech 2015 < Bitcoin Explained < Bitcoin-designs < Bitcoin Magazine < Andreas Antonopoulos < Austin Bitcoin Developers < Advancing Bitcoin < Baltic Honeybadger < Misc < Chaincode Labs < Lets Talk Bitcoin Podcast < Greg Maxwell < Bit Block Boom < Árboles de sintaxis abstracta merkleizados - MAST

Árboles de sintaxis abstracta merkleizados - MAST

Fecha: March 6, 2018

Transcripción De: Bryan Bishop

Traducción Por: Blue Moon

Tags: Taproot, Covenios, Validación

Categoría: Core dev tech

https://twitter.com/kanzure/status/972120890279432192

Ver también http://diyhpl.us/wiki/transcripts/bitcoin-core-dev-tech/2017-09-07-merkleized-abstract-syntax-trees/

Cosas de MAST

Podrías merkleizar directamente los scripts si cambias de IF, IFNOT, ELSE con IFJUMP que tiene el número de bytes.

Con graftroot y taproot, nunca para hacer cualquier scripts (que eran un hack para empezar las cosas). Pero estamos haciendo la validación y el cálculo.

Tomas todos los caminos que tiene; así que en lugar de eso, se convierte en … cierta condición, o ciertas no condiciones… Tomas todos los posibles ifs, usas esto, dices que es uno de estos, luego especificas cuál, y lo muestras y todos los demás pueden validar esto.

Si estás haciendo pactos por medio de mostrar la jerarquía dentro de un taproot, bramc hizo el interesante punto de que podrías tener una tasa de decaimiento adjunta para que la cantidad disminuya con el tiempo. Usted puede tener un mandato cualquier pacto que hace cumplir un pacto, que es explícito en taproot; usted podría decir que la cantidad que se hace cumplir es menor o igual a la cantidad…. usted podría hacer que sea monótona haciendo que disminuya en 1 satoshi cada vez. Usted debe elegir un número como, algún número de nivel de polvo. Podrías hacer algo relacionado con el polvo/peso. Y cómo se sale de las situaciones de bloqueo. El nuevo esquema de taproot hará que los pactos sean aún mejores. Hay taproot, graftroot, y luego cómo combinar esto con MAST para hacer este crecimiento logarítmico, todas las condiciones pueden ser representadas como funciones fidxed, te comprometes con algún script pero podrías deshacerte del script, en su lugar te comprometes con un árbol de merkle de posibles condiciones cada una de las cuales especifica las condiciones que serían aplicadas, no es un script que texecutes, es sólo una estructura de datos donde el primer campo podría ser el tiempo de bloqueo relativo, el segundo campo podría ser una lista de pactos vacíos, etc. Si quieres pactar una salida, el taptree se parece a esto o algo así, eso es lo que estás diciendo. Muestras una vía de derivación, Lo que no te importe, lo rellenas con un testigo sobre el gasto. La salida podría ser cualquier cosa, siempre y cuando puedas mostrar una prueba de merkle para la parte que sí te importa. Adjunto a ese pacto sería una cantidad… y tal vez otras propiedades… tal vez adjuntar la lógica aritmética a esa cantidad, y entonces es sólo decreciente en tamaño, o la suma de todos los pactos introducidos por una entrada es sólo decreciente en tamaño, y luego en la teoría matemática que han eliminado los pactos perpetuos, y si usted ata esto en los límites de polvo, entonces usted ha eliminado prácticamente los pactos no gastables.

Debate sobre MAST

MAST (merkleized abstract syntax tree) es un árbol de sintaxis en el que, si lo conviertes en un árbol de merkle, no tienes que revelar todo el árbol. Esta idea ha cambiado un poco a lo que ahora llamamos MAST. Es bastante diferente de la prupuesta original. La idea original era que teníamos “si alguna condición”, y luego la cláusula, y luego otra, y luego alguna otra cláusula. Lo que haríamos es que haríamos un hash de la secuencia de comandos (el condicional) y si resultara que el testigo, esta cláusula de aquí, revelaríamos esta otra secuencia de comandos. Esa era la idea original. Resulta que esto es básicamente lo mismo que… podría dividir esto en dos posibles scripts. Está la condición de aserción, y luego tienes algo llamado la “cláusula entonces”. Y luego tienes la “condición assert not” que es la cláusula else. Y así rompiste un script, que tenía ejecución condicional en dos scripts que están completamente linealizados. Esto puede generalizarse para los bucles. Mientras tengamos cualquier ejecución lineal de un script, entonces puedes dividirlo en todos los posibles caminos de exeucción. Obtienes una condición en la que las monedas podrían ser gastadas por uno de estos posibles scripts, incluso una vez linealizados.

El siguiente paso sería hacer un hash de cada una de las posibilidades y hacer un árbol de merkle, y luego revelar cuál de las posibilidades quieres gastar. Ya no tienes árboles de sintaxis, pero seguimos llamándolos MAST. Esto es lo que proporcionan bip116 y bip117. Voy a ser específico en estos, y luego alguien puede hablar de bip114.

bip116 proporciona OP_MERKLEBRANCHVERIFY (MBV) y toma una cuenta del número de elementos que está sacando del árbol de merkle. Toma la raíz. Toma una prueba y toma el elemento que está sacando. Así que (elemento, prueba, raíz, recuento de elementos, opcode MBV). La cuenta de elementos es el número de elementos, es útil para sacar un número de elementos. Se valida. Esto es lo que proporciona bip116.

En bip117, proporciona semántica de llamada de cola, porque vamos a emular algo que se parece a p2sh. En p2sh, si mi memoria no me falla, es “hash160 equal”. Si quieres ver cómo esto se conecta a tail-call, voy a ir a través de lo que esto hace. Hay una secuencia de comandos empujado a la pila, hash, ve si es igual a hte hash de la scriptpubkey, y eso es todo. Ahora, la forma - si usted fuera a ejecutar realmente esto, hacemos la coincidencia de la plantilla, pero si tuviéramos que ejecutar realmente, que terminaría con uno, el verdadero valor en la pila, y consumir la secuencia de comandos. Si sólo modificamos esto por el gusto de hacerlo, para decir, en cambio, vamos a poner un DUP y duplicar la secuencia de comandos. Así que si lo ejecutas, es el valor del script y el 1 en la pila. Y digamos que cambias el EQUAL por EQUALVERIFY para que consuma el resultado. Así que cuando se ejecute, comprobará que es el valor original comprometido, y dejará el script en la pila. Si primero empujamos todos los argumentos y el testigo para que el script se ejecute, va a terminar con una pila que se ve como arg1 arg2argN y luego el script en la pila. Y eso es lo que contendría la pila de testigos en este punto. La observación es que si tratamos cualquier situación en la que tenemos dos o más elementos en la pila, como una situación de llamada de cola en la que saltamos el último elemento y recurrimos a él como un script, esto haría que este p2sh modificado funcionara, y ya no sería un caso especial. Esto sería proporcionar una función hash para la forma de comprobar que el pre-compromiso coincida con el script. Cuando hay más de un elemento en la pila, ¿se trata el último elemento de la pila como un script? Esa es la idea general de la llamada de cola, y la propuesta es que sólo lo hagamos una vez. ¿Cómo se impone una profundidad de uno? Eso es fácil… Es compatible con el soft-fork, si quisieras hacerlo, y aún no estoy convencido de que sea una gran idea, podría ser una aplicación interesante en algunos lugares pero es algo que bitcoin necesita no estoy seguro, para esa implementación. ¿Necesita un límite de empuje para el script? Una desventaja de esto es que este script sería empujado como un elemento, que sería 512 bytes. Eso se puede arreglar en el futuro con otra versión del script. El límite de empuje para el script es molesto porque hay que incluir las diferentes condiciones. De todas formas ya hay un límite de 200 opcode… El código de deserialización de los testigos de Segwit impone algo aquí… para todos los elementos menos el último. Hay un límite estricto de que todos los elementos de la pila durante la ejecución real están limitados a 520 bytes. Esto sólo es cierto para la versión 0, por supuesto. La versión 1 no tiene ninguna regla de ejecución asociada. Es una limitación temporal.

Si nos hubiéramos dado cuenta de esto de antemano, podríamos haber hecho P2SH de antemano de esta manera. La recurrencia de la llamada de cola habría funcionado. Como descubrimos con segwit…. Cuando se quiere hacer p2sh, se … 160 bits de seguridad para la función hash… Si lo hubiéramos hecho así, podrías hacer 256 bits de seguridad o algo así con un opcode HASH256 o algo así… Podrías proporcionar cualquier método para mapear el compromiso en el scriptpubkey al script real que vas a ejecutar.

La conexión con MAST es que, si te parece bien hacer esto con P2SH, la idea es usar bip116 con merklebranchverify. En lugar de duplicar y hash (DUP HASH256), empujamos esta raíz, y comprobamos que el script proporcionado en la pila de testigos coincide. Se convierte en merklebranchverify y algunos opcodes DROP al final. Se consumen los elementos, se comprueba que era lo que se pensaba que era, se sueltan, es un opcode soft-fork, y luego se recurre a él. En ese punto, las cosas se complican en los BIPs porque resulta que en segwit decidimos no permitir más de un elemento en la pila o de lo contrario la ejecución está programada para fallar. Esa es una regla real aplicada por consenso. Esto es algo que podríamos arreglar en la versión 1. En la versión 0, no necesitamos gastar una versión de segwit… hay un truco en el BIP, que podríamos empujar a la pila de alt. Cualquier parámetro que tengas, lo envías a la pila alt, y luego cuando hace la comprobación de si hay elementos en ambas, mira en ambas, y luego mira el tamaño de los elementos en ambas pilas.

Originalmente la idea sería aplicarla a todos los scripts. Un problema que surge es que ya no se puede introspeccionar el script que se va a ejecutar, utilizando el análisis estático, con el fin de comprobar los recuentos de sigop o algo así. Esto podría llevar a posibilidades de DoS para cualquier cosa excepto segwit v0. Podrías tener giros válidos de UTXOs enteros que dejan de ser válidos si… si tienes algún UTXO antiguo que fallaría cleanstack, entonces este general… cleanstack sólo está activo para segwit y consensus. Podría haber cosas pubkey único … que existe una firma válida para .. pero falla cleanstack. Miré a través de todo testnet y mainnet, y en testnet parecía que podría haber algo raro.

En p2sh, la limpieza de la pila no es consensuada. No querrías volver a hacer un recurse si quedara algo en la pila… Hubo vectores de DoS para p2sh, obvio para scritpubkey bare script… Sólo quiero asegurarme de que no haces algo no gastable. La única situación en la que harías algo no gastable, es si hay un script segwit v0 que dejó algo en la pila alt. Nadie usa la pila alt. Lo he comprobado. Pero no puedes asumir que nadie usa la pila alt, por p2sh.

Hay un punto importante: no se pueden manipular las cosas en la pila de los alts. Puedes empujar y tirar. La única situación en la que las cosas permanecen en la pila secundaria es la de un scrpit que empujó algo allí y luego se olvidó de ello. En la práctica, ¿por qué alguien lo haría? ¿Tal vez alguien lo hizo, comprometió monedas a tal UTXO ya? Tal vez limitar la regla sólo para segwit o algo así. Oh, así es. Razones de DoS por las que queremos quitarlo de bare scriptpubkey y p2sh y que sólo se aplique a cualquier forma de segwit o algo así, y P2WSH también funciona o lo que sea el P2SH envuelto.

En el bip, llamo la atención que no hay conteo de sigop para el subíndice. No hay conteo de opcode. Creo que eso es correcto. No se puede saber de antemano cuáles van a ser, sin ejecutar un intérprete de scripts y ver en qué estado terminas. Puedes hacer números sobre qué tipo de denegación de servicio puedes construir haciendo esto, como llenarlo de validaciones de firmas o algo así. No es mucho más que un factor de tamaño constante de los bloques normales esperados… pero si estás preocupado, podrías introducir un límite de evaluación de scripts, que mire el tamaño de las pilas o de los scripts, y diga que no ejecutaré más que ese tamaño de veces o dividido por 64 checksigs o algo así, y ese es el límite que se aplica. Esto limitaría el tamaño de los tipos de transacciones que se pueden utilizar aquí.

¿Qué hay de hacer un árbol de suma de merkle de… Miré los árboles de suma merkle de esto… bip98, es una dependencia de bip116. bip98 especifica una estructura de árbol merkle más eficiente y compacta que tiene incluso con las optimizaciones de Pieter, siguen siendo 2,24x mejor que eso, que es una modificación del código de Satoshi. Y, lo hace no haciendo sha256 completo, hace una especie de sha256 con actualizaciones y no tiene la duplicación … eso es lo que nos da nuestra … el error de duplicación es en realidad un poco malo porque uno de los casos de uso de MBV es que, digamos que usted tiene un 2-de-cualquier número de claves que se requiere para firmar … si usted tiene error de duplicación, entonces usted podría tener tantos como usted quiere de su propia clave, por lo que, o bien hacer la lógica de validación en el lado de consenso a … o cambiarlo. Eso es lo que es bip98. Eso es micro-optimizar los árboles merkle, y puedes obtener un factor de 2 en el rendimiento, o disminuyes el tamaño del factor de seguridad el hashing.

Russell O’Connor tiene una estructura de árbol merkle diferente que ha estado mirando. Podrías hacer una inicialización estándar, hacer un bloque con cualquier información que quieras poner ahí, y luego mantener ese estado. Los árboles merkle, en bip98, usan un hashing separado del sha256, porque primero se hace el hashing de un bloque de información… no quieres estar atascado con una estúpida implementación de sha256 en python.

jl2012 tiene la propuesta bip114. Es una versión restringida de bip116 y bip117 combinados. Pones un hash raíz, tienes una raíz merkle de los diferentes scripts, pones el hash raíz en el scriptpubkey. Y luego para expandir, proporcionas uno de los scripts y la ruta y también la posición. Y entonces se ejecutará el script. Es una versión restringida en el sentido de que sólo lo hace una vez. Y ocurre con ese patrón específico. Creo que la principal diferencia es que bip114 permite el análisis estático. Es como p2sh básicamente, proporcionas una ruta y una posición. Si te comprometes por adelantado a decir que esto va a ser un formato estandarizado como la versión 1 de segwit, no importa el enfoque, puedes hacerlo. Donde los diferentes grupos difieren aquí, es en si debemos tomar ese enfoque por adelantado, o introducir una primitiva y luego ver cómo se utiliza.

En bip117, es una variación de OP_EVAL. Si podemos hacer 117, entonces no veo por qué no podríamos hacer OP_EVAL. Pero requiere empujar el estado. Si vas a hacer algún tipo de evaluación a su vez al contexto original, eso es un desafío de implementación mucho más grande. Tail-call es más fácil porque se tira el estado anterior. Tenemos la historia de bip17, donde … o bip12 tal vez. La implementación estaba mal y era vulnerable. Nos asustamos apropiadamente por eso, y entonces fuimos con p2sh en su lugar. El conservadurismo es la razón por la que podría preferir tail-call sobre OP_EVAL. Si has generalizado p2sh, entonces cualquier programa OP_EVAL puede ser transformado en un programa de recursividad tail-call, asumiendo que no requieres la completitud turing o algo así. Puedes reestructurar tu script de manera que dejes las cosas restantes en la pila.

Relación entre MAST y taproot, estado actual de los pull requests. bip114 es más eficiente en el sentido de que no es necesario volver a calcular el hash. Así que se pierde un nivel del árbol que necesita ser revelado, efectivamente. ¿Esto va a ser un modo MAST templado, o un modo MAST programable? bip114 tiene otras cosas sin embargo. También tiene opcodes deshabilitados y otras cosas. ¿Va a ser una ejecución programable, o raíces templadas.

¿Qué pasa con OP_CHECKINPUTVERIFY y, a continuación, si usted tiene - es difícil de conseguir completamente correcto, pero se puede decir, me gustaría ser emparejado con algún conjunto de otras entradas. Y entonces otra entrada podría decir que le gustaría ser emparejada con otras entradas, y entonces te permite pre-firmar combinatoriamente, estarías firmando con un conjunto de otras cosas, y no necesitas firmar m por m transacciones, puedes simplemente especificar una entrada que puede ser emparejada en cualquier combinación. Esto te permite hacer transacciones pre-firmadas y luego hacer un control de monedas sin que se dispare exponencialmente el número de transacciones pre-firmadas. Y puedes comprobar que una entrada existió, desde el script. Y tienes que ser capaz de poner el compromiso a la entrada, fuera del txid. Si está dentro del txid, entonces no puedes como, no puedes, uno tiene que venir antes que el otro y se vuelve dependiente del otro. El compromiso con el árbol de las entradas que querría ser emparejado, tiene que estar fuera del txid para la salida que estás…. Un ejemplo es, digamos que tengo una transacción, y quiero que pase, y tiene algún conjunto de entradas, y tengo un montón de transacciones de 1 bitcoin, y sólo quiero gastar una de ellas, y no quiero requerir ninguna en particular. Usted sabe la salida de antemano. Lo que esto le permitiría hacer es, que le permitiría - usted puede, las transacciones por lotes…. Usted estaría usando esto. En lugar de tener como, el sighash especificar a mirar las entradas y eso es lo que terminan de firmar, se pone que en la secuencia de comandos. Lo que esto te permitiría hacer es, elegir de una lista y hay una especie de casos de uso, esto es algo en Andrew Miller estaba trabajando en esto simultáneamente, como sus loterías que requieren que usted tenga un número de entradas potencialmente y es exponencial blow-up … La limpieza para el blow-up en el caso de amiller es difícil si no imposible de hacer en bitcoin. ¿Cuál es el caso de uso de la lotería? El caso de uso es que hay gente apostando, como 1000 personas, todas las cuales quieren poner cada una una moneda, y al azar uno de nosotros va a obtener los 1000 BTC y deseamos hacer esto sin querer apostar cantidades masivas de cosas, pero deseamos hacerlo sin apostar extra, y queremos que se haga de una manera que sea auditablemente justa para todos nosotros. Andrew Miller ideó una forma inteligente de hacer esto, que tiene buenas propiedades de escalado en Ethereum, pero tiene un problema de explosión exponencial en Bitcoin, que podría arreglarse si se pudiera hacer que los txid no tuvieran en cuenta toda la información sobre cómo llegaron a ser. Tienes brackets, y pares de personas, uno de ellos gana, y luego haces más pares, y para preparar esto de antemano, necesitas firmar cosas de antemano para todas las posibles formas en que los brackets podrían terminar, sin tener este blow-up exponencial. No sé si eso fue una tangente. Otro caso es el control de las monedas y la seguridad. No tienes que compartir las claves para ellos. Esto es ser capaz de especificar todas las transacciones, sin exponencial blow-up.

¿Graftroot vs MAST v2?

¿Cómo cambia esto de MAST a la luz de taproot y graftroot? ¿Y cuál es el estado actual de la implementación?

bip116 y bip117 tienen una implementación que está en github. El PR está actualmente activo. Creemos que la implementación es correcta y completa. Ha sido revisada por maaku, kallewoof, por algunas personas dentro de Blockstream y no voy a dar nombres porque podría equivocarme, no ha tenido mucha revisión externa todavía. Hubo una discusión en la lista de correo, supongo. Eso es algo que alimenta el tema de la prioridad de la revisión. Por lo que sé, están listos.

Taproot y graftroot en mi mente cambia sólo un aspecto de esto. Creo que son fundamentalmente compatibles entre sí. No se trata de una situación de o bien o bien. MAST se beneficia mucho de que se despliegue de forma que la gente pueda utilizarlo. Lo que podría estar utilizando, taproot a un compromiso con una secuencia de comandos que es en sí mismo un MAST, y esto consigue que el registro de tamaño de escala … O son exactamente compatibles, o se obtiene una versión más compacta con taproot con las mismas estructuras merkle.

Las horquillas de consenso deberían estar separadas del proceso de liberación. Deben publicarse por separado. No deberían estar agrupados con las versiones. Tenemos unos 30 bits, y no deberíamos intentar agrupar características en los mismos soft-forks. Agrupar características parece un compromiso político. Esto no tiene introducción de versiones segwit. Esta propuesta de MAST parece interactuar principalmente con taproot. El valor por defecto debería ser usar graftroot. Tiene poco sentido usar MAST con graftroot porque ya se tiene esa funcionalidad. MAST con taproot tiene sentido. No. Graftroot es un mecanismo por el que se puede delegar la autoridad de firma a otra persona, sujeto a una condición tal vez, y esa otra persona tiene su propia autoridad de firma que podría utilizar algún tipo de MAST. Tal vez son un firmante de umbral y cuando no pueden tener todo el mundo en línea, por ejemplo.

¿Cuál es la aplicación de MAST? Generalizando los contratos inteligentes en bitcoin, se crean árboles de transacciones por adelantado, que se firman, se firma la raíz y luego se trabaja hacia arriba, y hay contingencias ya pre-firmadas. Cada uno de estos gastos está posiblemente condicionado por tiempos de bloqueo, o bloqueos de hash, y el tipo de contratos inteligentes que la gente hace, se reducen a estas primitivas. Cuando hablamos de hacer un MAST o taproot, tenemos todas estas formas en que las salidas podrían ser gastadas, y necesitamos alguna forma compacta de especificarlas en tiempo de validación, o afirmarlas en tiempo de validación, que son válidas. Luego están los sprites, las loterías cero colaterales, los rayos, … Las únicas primitivas, de todos nuestros contratos inteligentes se basan en que requiero una firma con este e, requiero un checklocktime, o un hash preimage. El hash preimage es al menos parcialmente subsumible por scriptless. Se puede tener una preimagen hash por script para free…. Si supiéramos que estaríamos restringidos a sólo esos (y no creo que lo estemos), y estoy seguro de que cualquier cosa que le guste a covenants podría estar en esto (y covenants se vuelve fácil con taproot), podrías decir que tenemos un árbol merkle y cada hoja es una combinación de un número de claves públicas agregadas y un locktime y un hash preimage, o cualquiera de estas otras opciones de la biblioteca. Parece que los convenios tienen cosas como checklocktimeverify, no añaden mucho a la lógica, sólo más aserciones sobre entradas y tamaños de salida y scriptpubkeys de salida y esas cosas.

¿Dónde queda el MAST con estos otros cambios futuros? Graftroot está bien en el sentido de que tiene un tamaño constante, pero hay inconvenientes, ya que requiere la interacción entre todos los firmantes, tienes que guardar esa información. La alternativa es taproot, pero es de escala lineal y tienes que revelar cosas linealmente… en algún momento, podríamos usar una construcción tipo MAST. El caso trivial para un solo elemento es…. Creo que se podría decir que MAST es un aumento natural de taproot en el sentido de que, ambos tienen la configuración no interactiva, y una vez que tienes la configuración interactiva, graftroot cubre todo.

Si se requiere un script para codificar el hecho de que hay incluso un MAST en el lugar, esto es inherentemente menos eficiente que decir que el hash que estoy revelando es una raíz de un árbol en primer lugar. Es un hash extra. Es exactamente un hash extra. Así que esto es alrededor de 40 bytes testigo. Digamos que tienes 2^n combinaciones que quieres formar en el mismo nivel.

Hay un modelo programable para el MAST y un modelo templado para el MAST. Este es el enfoque bip117 vs bip114. Ignorando taproot por un segundo, usted tendría una salida que es (en el modelo programable) es un hash del script de la forma “root merklebranchverify”. En el caso de la aproximación programada, sólo tienes una salida que está proporcionando la raíz. Y la entrada, ya sea dentro del script o en el testigo o en algún otro nivel de incrustación, tendrá en el modelo programable “proof and leaf scripts”… tienes que revelar para qué sirve este hash, pero luego tienes que proporcionar el proof, y el leaf script, y conectar esas cosas. En la entrada, en el enfoque de la plantilla, todo lo que tienes es la prueba y luego el script de la hoja. El hecho de que el merklebranchverify en el enfoque programable.. es siempre algo añadido, y esto es ortogonal a si usted está usando taproot.

En el caso de taproot, podríamos hacer el hash del árbol merkle de la cosa dentro del taproot… Se podría decir que, tienes taproot con la cosa siendo …. El punto de taproot es C + hash(c, root) o P = C + hash(script)…. Lo que entra en esa fórmula en la derivación de taproot, puede ser el modelo de programación o el modelo de plantilla. Lo de bip114 siempre será más eficiente ahí. La prueba podría estar vacía, y entonces el script es la raíz. Sólo verificar el hash de la secuencia de comandos es la raíz, un árbol con un elemento a continuación, la raíz se convierte en el scripthash. Y la ruta de acceso a la misma es simplemente null. La “prueba” contiene el camino, en esto. Contiene un hash por cada nivel de árbol al que se desciende. En el caso específico de taproot, ya estás haciendo un hash debido a la concatenación… así que ¿por qué no omitir el hash extra?

En el caso programable, tienes el hash extra de la raíz y merklebranchverify, no tiene sentido, son bytes extra. Correcto.

En bip117, se propuso por primera vez como sólo probar una cosa se sacó del árbol de merkle. Hay casos en los que podrías querer mostrar 2 o 3 elementos o algo así, multisigiendo y luego comprobando si los elemntos son los mismos, y tiene sentido hacer esto en un opcode. Digamos que quieres un multisig de 3 de 1000, y tienes un árbol de 1000 claves, y quieres dar una prueba de que estos son 3 elementos del árbol. Usted podría hacer una serialización altamente eficiente para una prueba para sacar múltiples elementos del árbol de merkle. En el caso de taproot, sólo sacas un elemento. En bip117, decidimos que no debería haber un caso especial para un solo elemento. Pero si vamos a tener taproot, entonces tiene sentido hacer un caso especial, así que ese es el único cambio que haría a bip117.

Puedes tener múltiples elementos en taproot… pero tendrías que revelar los múltiples elementos, así que pierdes la ventaja.

Podrías hacer merklebranchverify en la misma raíz varias veces, sólo costaría más en espacio. Sí, puedes hacerlo.

En bip117, puedes comparar pruebas por bits. Hay una codificación única para cada subconjunto del árbol. Si quieres comprobar que dos cosas sacadas son iguales, puedes compararlas, o puedes comparar las pruebas.

Con taproot, para una raíz merkle, ¿quieres P en la pila?

Las raíces en taproot, podría ser la raíz de un árbol, pero también podría ser pensado como un guión. La diferencia inferencial a la idea es menor si se habla de taproot comprometiéndose con un script. Pero no hay razón para que sea sólo un script, podría ser la raíz de un árbol de merkle.

Taproot demuestra por qué no se debe desplegar un enfoque de plantillas. MAST estaba listo en enero y podríamos haber hecho un mayor esfuerzo para sacarlo. Si lo hubiéramos hecho de forma planificada como en el bip114, en la v1 tal vez empujamos una raíz MAST, y resulta que taproot es una mejor MAST …. haces lo de MAST, obtienes una raíz, y luego la pones en la clave pública. Si hubiéramos llegado a taproot después de haber adoptado un enfoque MAST programable, ….. No. Digamos que hubiéramos hecho el enfoque programable desde el principio. Hace 2 meses, habríamos tomado bip114 y nos habríamos comprometido con una raíz MAST. Y luego, 2 semanas más tarde, nos dimos cuenta de que podemos hacer taproot, y eso subsume completamente el viejo modelo. ¿Pero las versiones del programa testigo? ¿Queremos seguir agregando complejidad? Siempre hay que mantener lo antiguo. Este es un enfoque de los cambios consensuados del que estoy a favor, deberíamos hacer cambios incrementales que añadan nuevas características, y si hay una variante más compleja pero más eficiente que requiera cosas que aún no tenemos experiencia operativa, entonces deberíamos considerarla. Estamos investigando sobre taproot y graftroot, y aún no hay PR. Tener una amplia variedad sin permiso de cómo la gente puede usar algo ayuda a la experimentación, una pregunta que me hago es qué pasaría si el script de bitcoin hubiera sido pubkeys desde el principio y luego ECDSA y no hubiéramos llegado a Schnorr o algo… No estoy seguro de si… eso contrasta con, normalmente el enfoque más flexible es más difícil de razonar, sobre todo porque de esto se diseña explícitamente para soportar cosas que no conoces en el momento de diseñarlo. Checksigverify no era una cosa cuando el rayo fue diseñado. No quieres añadir mucha complejidad a un enfoque programático que no es fácil de verificar. Hay un conflicto inherente entre la experimentación y el mecanismo de fomento donde no se sabe cómo se va a utilizar, que intrínsecamente hace que también sea más difícil de razonar, cuando usted mencionó bip117, estábamos quitando sigops y usted trae un argumento como que el peor de los casos no es mucho peor entonces te creo cuando dices eso, pero pone una carga en cualquier cambio futuro porque ahora tenemos que preguntarse acerca de cómo interactúa. Tendría que haber un ratelimitador para cualquier otra operación costosa. Por otro lado, también, por razones de fungibilidad, se quiere que el uso de la producción real sea bastante restringido. El enfoque de script sin mimblewimble está tratando de averiguar cómo obtener la mayor funcionalidad del menor número de características. Un enfoque restringido es más fácil de recomendar. Pero no queremos depreciar el enfoque programable, ¿verdad? Una cosa de MAST se parece exactamente a p2sh o p2wsh con elementos extra de la pila. Tenemos scripts que operan sobre el código que se está ejecutando, y esto me asusta mucho.

La agregación de firmas a través de soft-forks… Cada vez que se lanza un soft-fork, eso cambia los opcodes que se ejecutan. Así que, por ejemplo, cambiar un NOP en algo que… como CSV y CLTV eran soft-forks de este tipo. Digamos que tienes un checksig antes de tal opcode y uno después, y un nodo antiguo, siempre va a asumir que ambos están siendo ejecutados y va a esperar una firma que use ambos. Sin embargo, después de tu soft-fork, ahora este segundo ya no se ejecuta, y como resultado, tu firma agregada puede ser válida antes o después de los nodos del soft-fork porque tendrán una idea diferente de qué operaciones checksig se han ejecutado. Los nodos más antiguos no ven las nuevas verificaciones. Este es un mal ejemplo. Utiliza el return true y luego cambia a return false, ese ejemplo. Estás añadiendo un nuevo opcode que también hace comprobaciones de firma. Y ahora, obviamente, no se puede agregar las viejas firmas de los operadores checksig con los nuevos opcodes. Y esto no es un problema, pero debilita la fuerza de… si tienes múltiples despliegues que añaden un poco de funcionalidad y todos ellos implican un conjunto diferente de operadores checksig se han hecho, cualquier potencialmente, reduce el efecto de la agregación. Cada vez que la ejecución potencial de su secuencia de comandos cambia, entonces tal vez usted debe tener una nueva versión, y entonces sólo hay un agregado por versión…. Inevitablemente, … “esta es la última vez que se introducen los operadores checksig”, no hay manera de hacerlo.

Si supiéramos que estamos desplegando taproot y también quisiéramos que la gente que usa el script tradicional también usara schnorr, sería más eficiente agruparlos, que hacerlos por separado. O hacer primero taproot y luego Schnorr. Esto parece ser específico de la agregación. Verificar la semántica soft-fork por sí sola podría estar bien. La idea es eliminar ese requisito. La idea detrás de segwit era permitirnos hacer más tipos de soft-forks…. Si no se verifica, entonces el script es inválido. Así que cada vez que hay una diferencia, la transacción es inválida. Un poco más de contexto: algo de lo que se habló antes, es que, cambiar las construcciones OP_NOPs en OP_RETURNTRUE, es muy incompatible con esa idea.

¿Por qué no proporcionar datos extra del testigo a …. que estás haciendo una agregación, en la nueva versión, no puedes proporcionar en el testigo las pubkeys extra que van a ser agregadas en? Sí. Esta es una de las formas en las que podrías tratar con la agregación entre versiones, donde tienes un bloque con decir, aquí hay una firma agregada, y aquí está la lista de todas las claves públicas con las que está, y entonces haces tus scripts, las afirmaciones de esta clave pública deben ser incluidas en esa lista, pero no es muy eficiente. Y necesitas los mensajes, así que básicamente no tienes nada. Se convierte en información testigo maleable, por lo que hay ataques DoS. No es bueno. No deberíamos hacer nada de eso, y tener un número de versión, tal vez un número de versión dentro de un testigo no necesita ser el mayor fuera del scriptpubkey, y siempre que el posible conjunto de agregados cambie, debes usar un nuevo número de versión, y entonces hay por número de versión en los agregados, creo que esto está bien. No hay que empecinarse en meter todo en un solo agregado, eso no va a funcionar a largo plazo de todas formas, pero hay que tener en cuenta que muchas etapas incrementales son menos eficientes que hacer varias cosas a la vez. Cuando incrementas el número de versión, sólo estás agregando entre esa versión; para cada versión, sólo hay un agregado en la transacción.

bip117 debería ser una prioridad de desarrollo, no de investigación. Esto también se aplicaría a taproot y graftroot. ¿Cuál es el listón para fusionar cualquiera de ellos?

El límite de sigops sigue siendo una preocupación, a pesar de que bip114 y bip116+bip117 está implementado. No está listo para la fusión, debido a esto. Contar los sigops… ¿el método real en el pull request? Va a tomar más de un desarrollador y decir, aquí es lo que el límite que realmente queremos, antes de añadir un límite más a las normas de consenso. Sabemos un buen límite. Es un límite conservador.