-
En el capítulo anterior tuvimos una visión simplificada de los HTLC, con suerte eso te dio una comprensión del mecanismo general y los incentivos en juego. En este capítulo profundizaremos en cómo se ven los HTLC en más detalle y cómo encajan en las transacciones de compromiso. De nostr:naddr1qqxnzdenxgerzve5x5enyv3sqyg8wumn8ghj7mn0wd68ytnhd9hx2q3qtx0k0a7lw62vvqax6p3ku90tccgdka7ul4radews2wrdsg0m865sxpqqqp65wc4f8xj aprendimos sobre las transacciones de compromiso. Aquí hay un breve resumen: Las transacciones de compromiso son asimétricas: Alice y Bob (participantes de un canal) cada uno tiene sus propias transacciones de compromiso. La transacción de compromiso que cada participante tiene se ve ligeramente diferente a la de su par en que cualquier salida que vaya al nodo local debe estar gravada por un bloqueo de tiempo relativo de `to_self_delay`. Esto es para darle a la otra parte la oportunidad de gastar a lo largo del camino de revocación de la salida si lo necesita. Con esto en mente, continuemos con el ejemplo del capítulo anterior donde Alice está enviando 2 BTC a un destinatario usando su canal con Bob como el primer salto en la ruta. Recuerda que a Alice se le ha dado un hash `H` al que necesita pagar. En este ejemplo, Alice es la *ofertante* de HTLC y Bob es el *receptor* de HTLC. ### Alice - Ofertante de HTLC Enfoquémonos en cómo Alice construirá su transacción de compromiso para incluir ahora el HTLC. La transacción de compromiso tendrá tres salidas: - Una salida de 5 BTC a Bob, gastable inmediatamente - Una salida de 3 BTC con dos posibles caminos de gasto: 1. Una gastable por Alice después de un `to_self_delay` 2. Una gastable inmediatamente por Bob si tiene la clave de revocación requerida (ver nostr:naddr1qqxnzdenxgerzve5x5enyv3sqyg8wumn8ghj7mn0wd68ytnhd9hx2q3qtx0k0a7lw62vvqax6p3ku90tccgdka7ul4radews2wrdsg0m865sxpqqqp65wc4f8xj y nostr:naddr1qqxnzdenxgerzvek8qenyvekqyxhwumn8ghj7mn0wvhxcmmvqgs9n8m87l0hd9xxqwndqcmwzh4uvyxmwlw0637kuhg98pkcy8ana2grqsqqqa282rd072 si necesitas un recordatorio) - Una salida de 2 BTC con... hmm, pensemos juntos sobre lo que necesita suceder aquí. ![c7_1](https://cdn.satellite.earth/9ac648eb3321e810901a59b96af4256060234353245f0069747d5c2f4bc251b3.png) Esta salida es donde debe suceder la magia del HTLC. Necesitamos los siguientes caminos de gasto en esta salida: - Debe ser gastable por Bob si tiene la preimagen de `H`; este es el camino bloqueado por hash - O gastable por Alice después de `cltv_expiry` - O gastable por Bob inmediatamente si tiene la clave de revocación PERO recuerda que las salidas de Alice hacia sí misma siempre deben tener un bloqueo de tiempo relativo de `to_self_delay` incluso después de `cltv_expiry`. Así que actualicemos los caminos de gasto con esta información: - Debe ser gastable por Bob si tiene la preimagen de `H`; este es el camino bloqueado por hash - O gastable por Alice después de `cltv_expiry` *Y después del retraso relativo `to_self_delay`* - O gastable por Bob inmediatamente si tiene la clave de revocación Todavía hay un problema: gravar la salida a Alice con ambos bloqueos de tiempo podría, en el peor de los casos, extender el tiempo de espera del HTLC por `to_self_delay`. Es decir, Bob podría tener un bloque adicional de `to_self_delay` para barrer la salida bloqueada por hash a pesar de que el HTLC esté técnicamente expirado. Así que en lugar de bloquear esta salida con ambas condiciones de bloqueo de tiempo, se bloquea solo por el `cltv_expiry`. Luego, en lugar de enviar fondos a Alice directamente, los fondos se envían a una transacción de tiempo de espera de HTLC separada (firmada por Alice y Bob) y esta transacción de tiempo de espera separada impone el `to_self_delay`. Esto permite a Alice bloquear el hecho de que el HTLC ha expirado y evita que Bob reclame la salida bloqueada por hash, todo mientras asegura que Alice solo pueda obtener sus fondos después de `to_self_delay` y, por lo tanto, aún permite a Bob gastar desde el camino de revocación (de la transacción de tiempo de espera de HTLC) si es necesario. La forma final de los caminos de gasto de la salida de HTLC de la transacción de compromiso se ve como sigue: - Un camino de gasto a Bob si tiene la preimagen de `H` (camino bloqueado por hash) - Un camino de gasto a Bob si tiene la clave de revocación - Un camino de gasto a una transacción de tiempo de espera de HTLC de segunda etapa La transacción de tiempo de espera de HTLC se construye así: - La transacción en sí está bloqueada por tiempo con `nLocktime` establecido en `cltv_expiry`. Esto significa que el camino de gasto en la transacción de compromiso original que envía a esta transacción de tiempo de espera de HTLC está efectivamente retrasado por `cltv_expiry` - La transacción tiene una salida con dos posibles caminos de gasto: 1. Uno a Alice después de `to_self_delay` 2. Uno a Bob si puede proporcionar la clave de revocación ![c7_2](https://cdn.satellite.earth/2e471ddfbcaabee7f04ab68b444c3258ac8708fa16a33d0057532e2f4e08b6ae.png) El script para la salida de HTLC de la transacción de compromiso de Alice (la ofertante de HTLC) se ve así: ``` # A nodo remoto con clave de revocación OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL OP_IF OP_CHECKSIG OP_ELSE <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL OP_NOTIF # A nodo local a través de la transacción de tiempo de espera de HTLC (bloqueada por tiempo). OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG OP_ELSE # A nodo remoto con preimagen. OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY OP_CHECKSIG OP_ENDIF OP_ENDIF ``` Puedes ver que el primer camino es el camino de revocación, el segundo es el camino a la transacción de tiempo de espera de HTLC (el camino bloqueado por tiempo) y el tercero es el camino bloqueado por hash. El script de salida de la transacción de tiempo de espera de HTLC se ve así: ``` OP_IF # Transacción de penalización <revocationpubkey> OP_ELSE `to_self_delay` OP_CHECKSEQUENCEVERIFY OP_DROP <local_delayedpubkey> OP_ENDIF OP_CHECKSIG ``` ### Bob - Receptor de HTLC Ahora enfoquémonos en cómo Bob (el receptor de HTLC) construirá su transacción de compromiso para incluir el HTLC. La transacción de compromiso tendrá tres salidas: - Una salida de 3 BTC a Alice gastable inmediatamente - Una salida de 5 BTC con dos posibles caminos de gasto: 1. Una gastable por Bob después de un `to_self_delay` 2. Una gastable inmediatamente por Alice si tiene la clave de revocación requerida - Una salida de 2 BTC que es un poco más complicada; profundicemos. ![c7_3](https://cdn.satellite.earth/a4ad18c79dd34f58e81a22e97dcd50801c59dc50e2a552437f405fc1dcc8de46.png) Pensemos en qué caminos de gasto debería tener esta salida: - Un camino de gasto que Bob puede reclamar si tiene la preimagen de `H` (camino bloqueado por hash) pero dado que es una salida hacia sí mismo, necesita tener un `to_self_delay` - Un camino de gasto que Alice puede gastar inmediatamente si tiene la clave de revocación necesaria - Un camino de gasto que Alice puede gastar después de `ctlv_expiry` (camino bloqueado por tiempo) Nuevamente, tener dos bloqueos de tiempo diferentes en el mismo script plantea un problema: si Bob conoce la preimagen pero tiene que esperar `to_self_delay` bloques para gastar desde el camino bloqueado por hash, entonces hay una posibilidad de que este `to_self_delay` sea más largo que el `cltv_expiry` que Alice debe esperar para reclamar el camino bloqueado por tiempo. Esto significaría que Alice podría potencialmente gastar a lo largo del camino bloqueado por tiempo a pesar de que Bob tiene la preimagen. Por lo tanto, Bob necesita una forma de bloquear el hecho de que se usará el camino bloqueado por hash mientras aún retrasa su redención de los fondos por `to_self_delay`. Así que se utiliza una transacción de éxito de HTLC separada para esto, permitiendo a Bob gastar desde el camino bloqueado por hash a esta transacción de éxito de HTLC, que luego impondrá por separado la condición de `to_self_delay`. La forma final de los caminos de gasto de la salida de HTLC de la transacción de compromiso se ve como sigue: - Un camino de gasto a Alice si tiene la clave de revocación (camino de revocación) - Un camino de gasto a Alice después de `cltv_expiry` (camino bloqueado por tiempo) - Un camino de gasto a la transacción de éxito de HTLC si Bob puede revelar la preimagen de `H` (camino bloqueado por hash) La transacción de éxito de HTLC se ve así: - La transacción *no* está bloqueada por tiempo (a diferencia del caso de Alice) - La transacción tiene una salida con dos posibles caminos de gasto: 1. Uno a Bob después de `to_self_delay` 2. Uno a Alice si puede proporcionar la clave de revocación ![c7_4](https://cdn.satellite.earth/8ad93e6bf7e47d71f585e94af879487c235c5a001248b77a060b8878502bf97b.png) El script para la salida de HTLC de la transacción de compromiso de Bob (el receptor de HTLC) se ve así: ``` # A nodo remoto con clave de revocación OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL OP_IF OP_CHECKSIG OP_ELSE <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL OP_IF # A nodo local a través de la transacción de éxito de HTLC. OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG OP_ELSE # A nodo remoto después del tiempo de espera. OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_CHECKSIG OP_ENDIF OP_ENDIF ``` Puedes ver en el script anterior que el primer camino es el camino de revocación, el segundo es el camino a la transacción de éxito de HTLC (y también es el camino bloqueado por hash) y el tercero es el camino de gasto bloqueado por tiempo. El script de salida de la transacción de éxito de HTLC se ve así: ``` OP_IF # Transacción de penalización <revocationpubkey> OP_ELSE `to_self_delay` OP_CHECKSEQUENCEVERIFY OP_DROP <local_delayedpubkey> OP_ENDIF OP_CHECKSIG ``` ### La imagen completa ![c7_5](https://cdn.satellite.earth/d96b94c0b17071ad7497239b1a09b0d9bba2e7dab34e4799deb356987a6612c9.png) ### Revisión - La salida de HTLC en una transacción de compromiso difiere dependiendo de si el constructor de la transacción es el **ofertante de HTLC** o el **receptor de HTLC** - La salida de HTLC necesita estar gravada por dos bloqueos de tiempo, pero tener ambos bloqueos de tiempo en el script puede causar problemas - La solución es barrer la salida a una segunda etapa de **transacción de tiempo de espera de HTLC** o **transacción de éxito de HTLC** e imponer el `to_self_delay` en esta transacción ### Referencias - [BOLT3: Transacciones](https://github.com/lightning/bolts/blob/master/03-transactions.md#offered-htlc-outputs) - [Cosas de LN Parte 5: Profundización en HTLC](https://ellemouton.com/posts/htlc-deep-dive/) por nostr:nprofile1qqswrt9pnxatlplu49h6meld8svmwqt87wwvk256rqk07n6eu4qeh5gpz3mhxue69uhhyetvv9ujuerpd46hxtnfduqs6amnwvaz7tmwdaejumr0dszpfjtz