Nov 23, 2024

LN capítulo 5: Profundización en HTLC

En este capítulo construiremos sobre lo que aprendimos en el capítulo anterior sobre HTLC y veremos en más detalle cómo se ven realmente y cómo encajan en las transacciones de compromiso.

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 naddr1qvzqqqr4gupzqkvlvlma7a55ccp6d5rrdc27h3ssmdmael286mjaq5uxmqslk04fqyg8wumn8ghj7mn0wd68ytnhd9hx2qqdxymnxv3jxyengdfnxgerq86c9yy 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:

c7_1

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

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

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

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

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