Dec 31, 2024

LN capítulo 7: Operación normal de un canal LN pre-taproot

En este capítulo, nos adentraremos en los detalles técnicos sobre cómo se añaden y eliminan los HTLC durante el funcionamiento normal de un canal pre-taproot, así como en cómo podemos actualizar las tarifas y restablecer conexiones.

Contenidos

Visión general

En este capítulo cubriremos las operaciones normales de un canal. Esto implica entender cómo se añaden HTLCs a un canal y cómo los pares del canal se comprometen a un nuevo estado para incluir estos HTLCs. Luego cubriremos cómo se restablece el flujo normal de un canal después de una desconexión. Finalmente, cubriremos el flujo de cierre cooperativo del canal. Por cierto, todos estos temas están cubiertos en BOLT2.

Preliminares

Aquí hay algunos conceptos y flujos que hemos cubierto anteriormente en nuestra serie que podrían ser útiles para entender este capítulo. No dudes en volver a leerlos si necesitas un recordatorio.

Operación normal del canal

Configuración

Alice y Bob han abierto su canal con éxito. Ambos han visto que la transacción de financiación está confirmada y han intercambiado el mensaje channel_ready entre ellos para indicar que están listos para usar el canal. El estado de sus transacciones de compromiso asimétricas actualmente se ve así:

1-inicial

Ambas transacciones de compromiso gastan la salida de la transacción de financiación multisig 2-de-2. Dado que es un multisig 2-de-2, gastar de esta salida requiere firmas de ambos pares. Las firmas están representadas por las dos cajas en la parte superior de las transacciones de compromiso. Puedes ver en el diagrama que la transacción de compromiso de Alice tiene una firma de Bob representada por la caja azul; Bob tiene una firma de Alice para su transacción de compromiso representada por la caja verde. Cada transacción de compromiso también tiene las salidas to_local y to_remote que pagan a las partes respectivas su saldo actual del canal. Tanto Alice como Bob pueden firmar sus propias transacciones de compromiso en cualquier momento y transmitirlas a la red de Bitcoin. Esto sería un cierre forzado.

Con el fin de hacer que los próximos diagramas sean más fáciles de seguir, ignoraremos las transacciones de financiación así como las salidas to_local y to_remote por un tiempo, ya que nuestro enfoque inicial estará en añadir y eliminar salidas de HTLC. Así que el diagrama anterior ahora se simplifica a esto:

2-simplificado

  • Una transacción de compromiso roja, mostrada a continuación, representa transacciones de compromiso pasadas y revocadas. Si Alice o Bob firmaran y transmitieran una de estas, entonces la otra parte podría barrer todos sus fondos. Así que estas transacciones revocadas pueden considerarse efectivamente inválidas.
  • Una transacción de compromiso amarilla representa el último conjunto válido de transacciones de compromiso. Estas son las transacciones de compromiso que se transmitirían en un cierre forzado.
  • Finalmente, cada lado tiene lo que se puede considerar un "área de preparación" donde se pueden proponer cambios a la transacción de compromiso. Más adelante, cualquiera de los lados puede decidir cuándo quiere que su contraparte se comprometa a los cambios en su transacción de compromiso de preparación.

Sugerencia: si te gusta git, esto es muy parecido a un flujo de trabajo de git. Los commits pasados están desactualizados, pero cuentan una buena historia de lo que sucedió. Tus últimos cambios comprometidos representan el estado actual de tu proyecto. Cualquier cambio que aún no esté comprometido está en preparación.

3-leyenda

Añadiendo un HTLC

Cuando Alice o Bob quieren enviar un pago, necesitarían proponer la inclusión del HTLC a su par de canal. Esto se hace con el mensaje update_add_htlc:

update_add_htlc

Aquí hay una tabla sobre lo que significa cada uno de los campos del mensaje:

CampoDescripción
channel_idutilizado para comunicar en qué canal debe tener lugar este cambio.
idun identificador que siempre incrementa para este cambio propuesto del remitente.
amount_msatla cantidad que debe adjuntarse a la salida de HTLC.
cltv_expiryla altura del bloque en la que el HTLC debe expirar.
onion_routing_packet y blinding_pointdatos que el destinatario del HTLC utilizará para determinar a dónde enviar el pago a continuación.

Hay un punto contraintuitivo a tener en cuenta aquí: el remitente de este mensaje aún no coloca esta actualización en su propia área de preparación. Esto se debe a que la actualización aún está "pendiente del receptor" porque el remitente aún no ha recibido ningún reconocimiento del receptor para la nueva actualización. Imagina este escenario: el remitente envía una actualización y luego recibe inmediatamente un mensaje commitment_signed (más detalles sobre esto más adelante). En este caso, no debería haber ambigüedad sobre qué actualizaciones están incluidas en la transacción que se está firmando. Esperemos que esto tenga más sentido a medida que trabajemos a través del ejemplo.

Paso 1: Alice -> Bob: update_add_htlc(A1)

Supongamos que Alice envía a Bob un update_add_htlc. Llamemos a este HTLC A1 porque es el primero que Alice (A) está enviando a Bob. Si Bob está contento con todos los campos en el mensaje, entonces añade el HTLC a su transacción de compromiso en el área de preparación y Alice marca el HTLC como pendiente en el lado de Bob, pero aún no lo añade a su propia transacción de compromiso en preparación.

add_A1

Ten en cuenta que ninguno de los lados ha comprometido realmente este HTLC aún, así que si Bob es un nodo de enrutamiento para este pago, no debería enviar aún update_add_HTLC al siguiente salto en la ruta hasta que A1 haya sido irrevocablemente comprometido. La adición o eliminación de un HTLC solo se considera irrevocablemente comprometida una vez que ambas partes en el canal se han comprometido a la transacción de compromiso con o sin él.

Esto no se muestra en el diagrama simplificado, pero en realidad la salida principal de Alice en la transacción de compromiso en preparación de Bob (es decir, la salida to_remote) ahora tendrá el valor del HTLC añadido restado (junto con las tarifas para cubrir la salida extra). Si el HTLC termina teniendo éxito, entonces esta cantidad se añadirá a la salida de Bob y si termina fallando, entonces se volverá a añadir a la salida de Alice.

Paso 2: Alice -> Bob: update_add_htlc(A2)

A pesar de que aún no se han comprometido al HTLC A1, esto no les impide añadir más cambios al área de preparación. Alice está más que bienvenida a proponer un nuevo HTLC, A2, a Bob:

add_A2

Paso 3: Bob -> Alice: update_add_htlc(B1)

Del mismo modo, Bob puede sugerir un cambio, B1, a Alice:

add_B1

Comprometiéndose al estado actual

En algún momento, uno de los pares querrá asegurarse de que el otro par se haya comprometido al último conjunto de cambios y revocar el estado válido anterior. Esto se hace enviando el mensaje commitment_signed:

commitment_signed

CampoDescripción
channel_idutilizado para comunicar en qué canal debe tener lugar este cambio.
signaturela firma del remitente para la transacción de compromiso del área de preparación de la parte remota.
num_htlcsel número de HTLCs que el remitente espera que estén en la transacción de compromiso remota.
htlc_signaturesun array de firmas de num_htlcs que son las firmas del remitente para cada una de las transacciones de HTLC de segundo nivel que la parte remota necesitaría transmitir si alguna vez tuviera que cerrar el canal forzosamente.

Sugerencia: si necesitas un recordatorio sobre las transacciones de HTLC de segundo nivel y por qué son necesarias, consulta este [capítulo].

Paso 4: Alice -> Bob: commitment_signed

Supongamos que Alice envía este mensaje a Bob. Bob ahora tendrá todas las firmas requeridas de Alice para transmitir su transacción de compromiso en el área de preparación:

commit_sign_1

Observa que Alice sabía que su firma necesitaría cubrir los HTLCs A1 y A2. Esto está bien porque el transporte subyacente está garantizado para ser confiable y ordenado, lo que significa que si Bob recibiera el mensaje commitment_signed de Alice, entonces significa que definitivamente recibió sus mensajes update_add_htlc para A1 y A2. Alice y Bob saben que la firma no debería cubrir el HTLC B1 ya que Alice no ha enviado un reconocimiento para él aún.

Otra cosa a notar es que Bob ahora tiene en realidad dos transacciones de compromiso válidas ya que aún no ha revocado su estado anterior. Sin embargo, está incentivado a revocar su transacción de compromiso anterior ya que los HTLCs en el nuevo estado son uno de los siguientes:

  • Pagos a Bob mismo, lo que significa que se beneficia de comprometerse al nuevo estado. Si no se compromete al nuevo estado, entonces Alice tampoco lo hará y por lo tanto aún existiría una versión de una transacción de compromiso que no paga a Bob sus fondos entrantes.
  • De manera similar al punto anterior, si Bob está enrutando un pago, entonces también está incentivado a comprometer irrevocablemente el HTLC ya que ganaría tarifas de enrutamiento si el pago tiene éxito.
  • Finalmente, si Bob está haciendo el pago él mismo, entonces el primer estado sería de hecho más rentable para él ya que tiene menos fondos en el segundo estado. Sin embargo, el comerciante al que Bob está haciendo el pago no liberará los bienes que se están comprando a menos que primero se reciban los fondos, lo que no sucederá si Alice no pasa el HTLC, lo cual no hará a menos que se haya comprometido irrevocablemente. Así que nuevamente, Bob está incentivado a revocar su estado anterior.
Paso 5: Bob -> Alice: revoke_and_ack

En respuesta al commitment_signed de Alice, Bob envía el mensaje revoke_and_ack:

revoke_and_ack

  • El per_commitment_secret proporciona a Alice la información que necesita para poder gastar cualquier ruta de revocación en el estado previamente válido de Bob. Consulta el [capítulo de revocación] para más detalles sobre cómo funciona la revocación.
  • El next_per_commitment_point le da a Alice la información que necesita para derivar la clave pública de revocación que se utilizará en la próxima transacción de compromiso de Bob.

Una vez que Alice recibe este mensaje, la transacción de compromiso anterior de Bob ha sido revocada con éxito. Ten en cuenta que este mensaje reconoce explícitamente el mensaje commitment_signed enviado por Alice y, por extensión, dado que la entrega de mensajes es confiable y ordenada, también reconoce implícitamente los mensajes update_add_htlc que Alice envió para A1 y A2. Por lo tanto, Alice puede finalmente añadir A1 y A2 a su transacción de compromiso en el área de preparación ya que ya no están pendientes en el lado de Bob:

8_revoke_1

Ahora podemos limpiar un poco el diagrama:

9_limpieza_1

En el último diagrama, observa que las últimas transacciones de compromiso de Alice y Bob están en realidad desincronizadas. Esto está bien ya que ninguna de las actualizaciones se ha comprometido irrevocablemente aún. Eso podría parecer difícil de creer ya que las transacciones de compromiso se ven tan diferentes, así que vamos a repasar las consecuencias de que cualquiera de estas transacciones termine en la cadena desde la perspectiva de ambos lados.

  • Desde la perspectiva de Alice:

    • si su transacción de compromiso se transmitiera, recuperaría su cantidad original to_local.
    • si la transacción de compromiso de Bob se transmitiera, entonces los HTLCs ofrecidos por Alice (como A1 y A2) estarían en la cadena. Para los HTLCs ofrecidos, Alice está enviando sats fuera, lo que significa que su to_local sería menor. Pero, si Bob era un nodo de enrutamiento para estos HTLCs, entonces no los habría reenviado ya que aún no están comprometidos irrevocablemente, lo que significa que no recibiría las pre-imágenes requeridas para reclamar estos HTLCs y, por lo tanto, Alice podría recuperar sus fondos a través de la ruta de tiempo de espera. Si Bob era el destinatario de estos HTLCs, entonces podría producir la pre-imagen para reclamar los HTLCs, pero entonces Alice vería la pre-imagen en la cadena y podría reclamar los HTLCs entrantes de su canal entrante y así habría ganado tarifas de enrutamiento.
  • Desde la perspectiva de Bob:

    • si la transacción de compromiso de Alice se transmite, Bob recupera sus fondos a través de la salida to_remote.
    • si Bob tuviera que cerrar forzosamente a través de su transacción de compromiso, entonces los HTLCs ofrecidos a él (como A1 y A2) estarían en la cadena. Si Bob estaba enrutando estos HTLCs, entonces no los habría reenviado ya que aún no están comprometidos irrevocablemente. Así que no podría reclamar la ruta de éxito, pero eso está bien ya que los fondos para estos no salieron de su saldo. Si Bob era el destino final para estos, entonces podría reclamarlos a través de la ruta de éxito.
Paso 6: Alice -> Bob: update_add_htlc(A3)

Queremos ser muy claros sobre el punto de que las transacciones de compromiso pueden permanecer desincronizadas indefinidamente y que Bob no necesita enviar commitment_signed solo porque Alice lo hizo. Así que, para el sake de otro ejemplo, supongamos que Alice en este punto envía otro HTLC, A3, a Bob:

10_add_A3

Paso 7: Bob -> Alice: commitment_signed

Bob quiere comprometer irrevocablemente algunos de los HTLCs para poder reenviarlos, así que finalmente envía a Alice un commitment_signed propio. Esto incluirá su firma para la transacción de compromiso del área de preparación de Alice junto con todas las firmas requeridas de él para las salidas de HTLC de segundo nivel.

11_commit_sign_2

Paso 8: Alice -> Bob: revoke_and_ack

Al igual que Bob hizo anteriormente, Alice responde al commitment_signed con un revoke_and_ack para revocar su estado anterior. Esto también sirve como un reconocimiento para Bob de que Alice ha recibido y se ha comprometido a B1, por lo que Bob ahora puede agregar B1 a su transacción de compromiso en el área de preparación.

12_revoke_2

Rápida limpieza...

13_clean_up_2

¡Por fin, tenemos algunos HTLCs comprometidos de forma irrevocable! A1 y A2 han sido comprometidos de forma irrevocable. B1 y A3, sin embargo, no lo han sido ya que aún no han sido comprometidos por ambas partes.

Comprometamos rápidamente B1 y A3 de forma irrevocable también.

Paso 8: Alice -> Bob: commitment_signed

14_commit_sign_3

Paso 9: Bob -> Alice: revoke_and_ack

15_revoke_3

Paso 10: Bob -> Alice: commitment_signed

16_commit_sign_4

Paso 11: Alice -> Bob: revoke_and_ack

17_revoke_4

Limpiado:

18_clean_up_4

¡Ahora todos los HTLCs han sido comprometidos de forma irrevocable!

Eliminando HTLCs

Probablemente ya entiendas la idea de agregar HTLCs. Pero, ¿qué pasa con eliminarlos? Los HTLCs se eliminan si un pago tiene éxito o si falla. Ten en cuenta que los mensajes de eliminación de HTLC solo pueden ser enviados por el par que no envió la actualización original update_add_htlc y que los HTLCs solo son removibles una vez que han sido comprometidos de forma irrevocable. Afortunadamente para nosotros, todos los HTLCs han sido comprometidos de forma irrevocable, así que podemos comenzar a eliminarlos ahora.

Paso 12: Bob -> Alice: update_fulfill_htlc(A2)

En el mejor de los casos, un HTLC se elimina porque se está cumpliendo, lo que significa que su pre-imagen se está devolviendo. Esto se hace con el mensaje update_fulfilled_htlc, que se ve así:

update_fulfill_htlc

En nuestro ejemplo, Bob envía a Alice el mensaje update_fulfill_htlc para el HTLC A2. Esto también demuestra que los HTLCs no tienen que ser eliminados en el mismo orden en que fueron agregados.

20_fulfill_A2

Ten en cuenta que, al igual que las actualizaciones para agregar un HTLC, las actualizaciones para eliminar un HTLC también estarán inicialmente pendientes en el lado receptor hasta que hayan sido reconocidas por un revoke_and_ack. Así que, en nuestro caso, Alice elimina A2 de su transacción en el área de preparación cuando recibe el update_fulfill_htlc (y asigna el monto del HTLC a la salida de Bob), pero Bob aún no elimina el HTLC de su transacción en el área de preparación.

A diferencia de otros mensajes de actualización, no hay necesidad de esperar a que una eliminación de HTLC sea comprometida de forma irrevocable si recibes la pre-imagen para ello. Después de todo, puedes enviar inmediatamente la pre-imagen hacia arriba para reclamar cualquier HTLC allí.

Paso 13: Bob -> Alice: update_fail_htlc(A1)

Los HTLCs también pueden ser eliminados debido a fallos en el pago, como el tiempo de espera de los HTLCs o si hubo algún tipo de fallo de enrutamiento, como que un canal específico en la ruta ya no exista, no se cumplan los requisitos de tarifa de un salto, un enlace no tenga saldo suficiente, etc. Tales fallos se comunican con el mensaje update_fail_htlc:

update_fail_htlc

El campo de razón es un blob cifrado para el remitente del pago para informarles sobre la razón del fallo.

Después de que Bob envía a Alice el mensaje update_fail_htlc para A1, el estado se ve como sigue:

21_fail_A1

Paso 14: Bob -> Alice: update_fail_malformed_htlc(A3)

El mensaje final que se puede usar para eliminar un HTLC es el mensaje update_fail_malformed_htlc:

update_fail_malformed_htlc

Este mensaje se envía si algún salto en la ruta de pago no pudo analizar el onion_routing_packet que recibió en update_add_htlc. Si Bob envía a Alice este mensaje para A3, entonces el estado ahora se ve así:

22_fail_A3

Paso 15: Alice -> Bob: update_fulfill_htlc(B1)

Alice también inicia la eliminación de B1 enviando un update_fulfill_htlc a Bob.

23_fulfill_B1

Ahora limpiemos las eliminaciones de HTLC comprometiéndolas de forma irrevocable. Esto requerirá algunos flujos de commitment_signed - revoke_and_ack:

Paso 16: Bob -> Alice: commitment_signed

24_commit_sign_5

Paso 17: Alice -> Bob: revoke_and_ack

25_revoke_5

Paso 18: Alice -> Bob: commitment_signed

26_commit_sign_6

Paso 19: Bob -> Alice: revoke_and_ack

27_revoke_6

Paso 20: Bob -> Alice: commitment_signed

28_commit_sign_7

Paso 21: Alice -> Bob: revoke_and_ack

29_revoke_7

Los dos estados válidos ahora se ven bonitos y limpios nuevamente:

30_clean_up_7

Actualizando tarifas

Hay un mensaje update_* más que necesitamos cubrir y ese es el mensaje update_fee. Este mensaje se utiliza para actualizar la tasa de tarifa que los pares deben usar al construir sus transacciones de compromiso. La tasa de tarifa original se decide en el flujo de apertura de canal, pero si la tasa de tarifa promedio del mempool aumenta, el financiador del canal podría decidir actualizar la tarifa de las transacciones de compromiso para que tengan una mejor oportunidad de ser confirmadas de manera oportuna en una situación de cierre forzado. También podría ser que cuando se abrió el canal, se eligió una tasa de tarifa muy alta y quizás se desearía una tasa de tarifa más baja.

Este mensaje sigue reglas similares a otros mensajes update_* en que también debe ser comprometido de forma irrevocable antes de que surta efecto. La única regla adicional que se aplica a este mensaje es que solo el financiador del canal puede enviar este mensaje:

update_fee

Ten en cuenta que con los canales de anclaje, la necesidad de usar el mensaje update_fee se está volviendo cada vez menor, ya que los nodos podrán usar CPFP en la transacción de cierre forzado si es necesario.

Retransmisión de mensajes

Algo que puedes haber notado en el flujo de agregar/eliminar HTLC es que los reconocimientos explícitos para los mensajes update_* se retrasan hasta el intercambio de commitment_signed/revoke_and_ack. Eso está bien la mayor parte del tiempo, ya que asumimos que el transporte subyacente entre los dos nodos (ver BOLT8) es ordenado y confiable. Sin embargo, si por alguna razón la conexión entre los dos nodos necesita ser restablecida, habrá dudas sobre si nuestro par ha recibido el último mensaje que enviamos. Aquí es donde entra el mensaje channel_reestablish. Al reconectarse, antes de continuar con el flujo de operación normal, los pares intercambiarán este mensaje para asegurarse de que están en la misma página y para determinar qué mensajes posiblemente necesitan retransmitir a su par.

channel_reestablish

Cada par en el canal tiene su versión de la transacción de compromiso y las dos transacciones de compromiso pueden actualizarse de forma independiente, lo que significa que el número de veces que el estado de la transacción de compromiso de un lado ha sido actualizado (a través del flujo de commitment_signed y revoke_and_ack) podría ser completamente diferente al del otro lado.

El campo next_commitment_number en el mensaje channel_reestablish nos permite comunicarnos con nuestro par sobre el próximo commitment_signed que esperamos recibir de ellos. De esta manera, ellos sabrán si quizás hemos perdido un commitment_signed de ellos que enviaron anteriormente antes de la desconexión. En otras palabras, next_commitment_number le dice al par remoto cuál es nuestro último estado comprometido.

De manera similar, next_revocation_number es el número de compromiso del próximo revoke_and_ack que esperamos recibir. En otras palabras, esto indica a nuestro par cuál número de compromiso creemos que es el más reciente.

El your_last_per_commitment_secret es el último secreto por compromiso recibido del par remoto, lo que le dará al par remoto una idea del estado que definitivamente ha revocado.

my_current_per_commitment_point es el punto de compromiso de la parte local en su última transacción de compromiso firmada por el par remoto (en otras palabras, la transacción de compromiso que aún no ha sido revocada).

Hay muchas verificaciones que un nodo debe hacer al recibir un channel_reestablish para asegurarse de que todas las actualizaciones necesarias se retransmitan para que el canal pueda continuar funcionando con normalidad. También hay algunas verificaciones que aseguran que los nodos no sean engañados para revocar un estado que aún no debería ser revocado o engañados para transmitir un estado que ha sido revocado. Si estás interesado en los detalles sobre estas verificaciones, lee BOLT2.

Ten en cuenta que cuando ocurre un restablecimiento de conexión, ambos lados deben eliminar cualquier actualización no comprometida de su área de preparación. Si volvemos a visitar la analogía de git, deberían usar git stash cuando ocurra una reconexión. Esto significa que ambos lados necesitarán retransmitir cualquier mensaje update_* que aún no haya sido comprometido en la transacción de compromiso del otro lado.

Referencias