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

commonMain.fr.acinq.lightning.blockchain.electrum.IElectrumClient.kt Maven / Gradle / Ivy

package fr.acinq.lightning.blockchain.electrum

import fr.acinq.bitcoin.BlockHeader
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.Transaction
import fr.acinq.bitcoin.TxId
import fr.acinq.lightning.blockchain.Feerates
import fr.acinq.lightning.blockchain.IClient
import fr.acinq.lightning.blockchain.fee.FeeratePerByte
import fr.acinq.lightning.blockchain.fee.FeeratePerKw
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

/** Note to implementers: methods exposed through this interface must *not* throw exceptions. */
interface IElectrumClient : IClient {
    val notifications: Flow
    val connectionStatus: StateFlow

    /** Return the transaction matching the txId provided, if it can be found. */
    suspend fun getTx(txId: TxId): Transaction?

    /** Return the block header at the given height, if it exists. */
    suspend fun getHeader(blockHeight: Int): BlockHeader?

    /** Return the block headers starting at the given height, if they exist (empty list otherwise). */
    suspend fun getHeaders(startHeight: Int, count: Int): List

    /** Return a merkle proof for the given transaction, if it can be found. */
    suspend fun getMerkle(txId: TxId, blockHeight: Int, contextOpt: Transaction? = null): GetMerkleResponse?

    /** Return the transaction history for a given script, or an empty list if the script is unknown. */
    suspend fun getScriptHashHistory(scriptHash: ByteVector32): List

    /** Return the utxos matching a given script, or an empty list if the script is unknown. */
    suspend fun getScriptHashUnspents(scriptHash: ByteVector32): List

    /**
     * Try broadcasting a transaction: we cannot know whether the remote server really broadcast the transaction,
     * so we always consider it to be a success. The client should regularly retry transactions that don't confirm.
     */
    suspend fun broadcastTransaction(tx: Transaction): TxId

    /** Estimate the feerate required for a transaction to be confirmed in the next [confirmations] blocks. */
    suspend fun estimateFees(confirmations: Int): FeeratePerKw?

    /******************** Subscriptions ********************/

    /** Subscribe to changes to a given script. */
    suspend fun startScriptHashSubscription(scriptHash: ByteVector32): ScriptHashSubscriptionResponse

    /** Subscribe to headers for new blocks found. */
    suspend fun startHeaderSubscription(): HeaderSubscriptionResponse

    /**
     * @return the number of confirmations, zero if the transaction is in the mempool, null if the transaction is not found
     */
    suspend fun getConfirmations(tx: Transaction): Int? {
        return when (val status = connectionStatus.value) {
            is ElectrumConnectionStatus.Connected -> {
                val currentBlockHeight = status.height
                val scriptHash = ElectrumClient.computeScriptHash(tx.txOut.first().publicKeyScript)
                val scriptHashHistory = getScriptHashHistory(scriptHash)
                val item = scriptHashHistory.find { it.txid == tx.txid }
                item?.let { if (item.blockHeight > 0) currentBlockHeight - item.blockHeight + 1 else 0 }
            }
            else -> null
        }
    }

    override suspend fun getConfirmations(txId: TxId): Int? = getTx(txId)?.let { tx -> getConfirmations(tx) }

    override suspend fun getFeerates(): Feerates? {
        return Feerates(
            minimum = estimateFees(144)?.let { FeeratePerByte(it) } ?: return null,
            slow = estimateFees(18)?.let { FeeratePerByte(it) } ?: return null,
            medium = estimateFees(6)?.let { FeeratePerByte(it) } ?: return null,
            fast = estimateFees(2)?.let { FeeratePerByte(it) } ?: return null,
            fastest = estimateFees(1)?.let { FeeratePerByte(it) } ?: return null,
        )
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy