All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.fr.acinq.lightning.transactions.CommitmentSpec.kt Maven / Gradle / Ivy

There is a newer version: 1.8.4
Show newest version
package fr.acinq.lightning.transactions

import fr.acinq.bitcoin.PublicKey
import fr.acinq.lightning.MilliSatoshi
import fr.acinq.lightning.blockchain.fee.FeeratePerKw
import fr.acinq.lightning.wire.*
import kotlinx.serialization.Transient

sealed class CommitmentOutput {
    data object ToLocal : CommitmentOutput()
    data object ToRemote : CommitmentOutput()

    data class ToLocalAnchor(val pub: PublicKey) : CommitmentOutput()
    data class ToRemoteAnchor(val pub: PublicKey) : CommitmentOutput()

    data class InHtlc(val incomingHtlc: IncomingHtlc) : CommitmentOutput()
    data class OutHtlc(val outgoingHtlc: OutgoingHtlc) : CommitmentOutput()
}

sealed class DirectedHtlc {
    abstract val add: UpdateAddHtlc

    fun opposite(): DirectedHtlc = when (this) {
        is IncomingHtlc -> OutgoingHtlc(add)
        is OutgoingHtlc -> IncomingHtlc(add)
    }

    fun direction(): String = when (this) {
        is IncomingHtlc -> "IN"
        is OutgoingHtlc -> "OUT"
    }
}

data class IncomingHtlc(override val add: UpdateAddHtlc) : DirectedHtlc()

data class OutgoingHtlc(override val add: UpdateAddHtlc) : DirectedHtlc()

fun Iterable.incomings(): List = mapNotNull { (it as? IncomingHtlc)?.add }
fun Iterable.outgoings(): List = mapNotNull { (it as? OutgoingHtlc)?.add }

data class CommitmentSpec(
    val htlcs: Set,
    val feerate: FeeratePerKw,
    val toLocal: MilliSatoshi,
    val toRemote: MilliSatoshi
) {
    fun findIncomingHtlcById(id: Long): IncomingHtlc? = htlcs.find { it is IncomingHtlc && it.add.id == id } as IncomingHtlc?

    fun findOutgoingHtlcById(id: Long): OutgoingHtlc? = htlcs.find { it is OutgoingHtlc && it.add.id == id } as OutgoingHtlc?

    companion object {
        fun removeHtlc(changes: List, id: Long): List =
            changes.filterNot { it is UpdateAddHtlc && it.id == id }

        fun addHtlc(spec: CommitmentSpec, directedHtlc: DirectedHtlc): CommitmentSpec {
            return when (directedHtlc) {
                is OutgoingHtlc -> spec.copy(
                    toLocal = spec.toLocal - directedHtlc.add.amountMsat,
                    htlcs = spec.htlcs + directedHtlc
                )
                is IncomingHtlc -> spec.copy(
                    toRemote = spec.toRemote - directedHtlc.add.amountMsat,
                    htlcs = spec.htlcs + directedHtlc
                )
            }
        }

        fun fulfillIncomingHtlc(spec: CommitmentSpec, htlcId: Long): CommitmentSpec {
            val htlc = spec.findIncomingHtlcById(htlcId)
            return htlc?.let { spec.copy(toLocal = spec.toLocal + htlc.add.amountMsat, htlcs = spec.htlcs - it) }
                ?: throw RuntimeException("cannot find htlc id=$htlcId")
        }

        fun fulfillOutgoingHtlc(spec: CommitmentSpec, htlcId: Long): CommitmentSpec {
            val htlc = spec.findOutgoingHtlcById(htlcId)
            return htlc?.let { spec.copy(toRemote = spec.toRemote + htlc.add.amountMsat, htlcs = spec.htlcs - it) }
                ?: throw RuntimeException("cannot find htlc id=$htlcId")
        }

        fun failIncomingHtlc(spec: CommitmentSpec, htlcId: Long): CommitmentSpec {
            val htlc = spec.findIncomingHtlcById(htlcId)
            return htlc?.let { spec.copy(toRemote = spec.toRemote + htlc.add.amountMsat, htlcs = spec.htlcs - it) }
                ?: throw RuntimeException("cannot find htlc id=$htlcId")
        }

        fun failOutgoingHtlc(spec: CommitmentSpec, htlcId: Long): CommitmentSpec {
            val htlc = spec.findOutgoingHtlcById(htlcId)
            return htlc?.let { spec.copy(toLocal = spec.toLocal + htlc.add.amountMsat, htlcs = spec.htlcs - it) }
                ?: throw RuntimeException("cannot find htlc id=$htlcId")
        }

        fun reduce(
            localCommitSpec: CommitmentSpec,
            localChanges: List,
            remoteChanges: List
        ): CommitmentSpec {
            val spec1 = localChanges.fold(localCommitSpec, { spec, u ->
                when (u) {
                    is UpdateAddHtlc -> addHtlc(spec, OutgoingHtlc(u))
                    else -> spec
                }
            })
            val spec2 = remoteChanges.fold(spec1, { spec, u ->
                when (u) {
                    is UpdateAddHtlc -> addHtlc(spec, IncomingHtlc(u))
                    else -> spec
                }
            })
            val spec3 = localChanges.fold(spec2, { spec, u ->
                when (u) {
                    is UpdateFulfillHtlc -> fulfillIncomingHtlc(spec, u.id)
                    is UpdateFailHtlc -> failIncomingHtlc(spec, u.id)
                    is UpdateFailMalformedHtlc -> failIncomingHtlc(spec, u.id)
                    else -> spec
                }
            })
            val spec4 = remoteChanges.fold(spec3, { spec, u ->
                when (u) {
                    is UpdateFulfillHtlc -> fulfillOutgoingHtlc(spec, u.id)
                    is UpdateFailHtlc -> failOutgoingHtlc(spec, u.id)
                    is UpdateFailMalformedHtlc -> failOutgoingHtlc(spec, u.id)
                    else -> spec
                }
            })
            val spec5 = (localChanges + remoteChanges).fold(spec4, { spec, u ->
                when (u) {
                    is UpdateFee -> spec.copy(feerate = u.feeratePerKw)
                    else -> spec
                }
            })
            return spec5
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy