Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import co.touchlab.kermit.Logger
import fr.acinq.bitcoin.*
import fr.acinq.lightning.SwapInParams
import fr.acinq.lightning.blockchain.fee.FeeratePerKw
import fr.acinq.lightning.crypto.KeyManager
import fr.acinq.lightning.crypto.SwapInOnChainKeys
import fr.acinq.lightning.logging.debug
import fr.acinq.lightning.logging.info
import fr.acinq.lightning.utils.sat
Expand Down Expand Up @@ -96,9 +96,9 @@ data class WalletState(val addresses: Map<String, AddressState>) {
}.coerceAtLeast(0)

/** Builds a transaction spending all expired utxos and computes the mining fee. The transaction is fully signed but not published. */
fun spendExpiredSwapIn(swapInKeys: KeyManager.SwapInOnChainKeys, scriptPubKey: ByteVector, feerate: FeeratePerKw): Pair<Transaction, Satoshi>? {
fun spendExpiredSwapIn(swapInKeys: SwapInOnChainKeys, scriptPubKey: ByteVector, feerate: FeeratePerKw): Pair<Transaction, Satoshi>? {
val utxos = readyForRefund.map {
KeyManager.SwapInOnChainKeys.SwapInUtxo(
SwapInOnChainKeys.SwapInUtxo(
txOut = it.txOut,
outPoint = it.outPoint,
addressIndex = it.addressMeta.indexOrNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package fr.acinq.lightning.blockchain.electrum

import fr.acinq.bitcoin.*
import fr.acinq.lightning.blockchain.fee.FeeratePerKw
import fr.acinq.lightning.crypto.KeyManager
import fr.acinq.lightning.crypto.Bip84OnChainKeys
import fr.acinq.lightning.logging.LoggerFactory
import fr.acinq.lightning.logging.info
import fr.acinq.lightning.transactions.Transactions
Expand All @@ -14,7 +14,7 @@ import kotlinx.coroutines.launch

class FinalWallet(
private val chain: Chain,
private val finalWalletKeys: KeyManager.Bip84OnChainKeys,
private val finalWalletKeys: Bip84OnChainKeys,
electrum: IElectrumClient,
scope: CoroutineScope,
loggerFactory: LoggerFactory
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package fr.acinq.lightning.blockchain.electrum

import fr.acinq.bitcoin.Chain
import fr.acinq.lightning.crypto.KeyManager
import fr.acinq.lightning.crypto.SwapInOnChainKeys
import fr.acinq.lightning.logging.LoggerFactory
import fr.acinq.lightning.logging.info
import kotlinx.coroutines.CoroutineScope
Expand All @@ -13,7 +13,7 @@ import kotlinx.coroutines.launch

class SwapInWallet(
chain: Chain,
swapInKeys: KeyManager.SwapInOnChainKeys,
swapInKeys: SwapInOnChainKeys,
electrum: IElectrumClient,
scope: CoroutineScope,
loggerFactory: LoggerFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import fr.acinq.lightning.blockchain.WatchTriggered
import fr.acinq.lightning.blockchain.electrum.WalletState
import fr.acinq.lightning.blockchain.fee.FeeratePerKw
import fr.acinq.lightning.channel.states.PersistedChannelState
import fr.acinq.lightning.crypto.KeyManager
import fr.acinq.lightning.crypto.ChannelKeys
import fr.acinq.lightning.utils.UUID
import fr.acinq.lightning.wire.FailureMessage
import fr.acinq.lightning.wire.LightningMessage
Expand Down Expand Up @@ -36,7 +36,7 @@ sealed class ChannelCommand {
val requestRemoteFunding: LiquidityAds.RequestFunding?,
val channelOrigin: Origin?,
) : Init() {
fun temporaryChannelId(keyManager: KeyManager): ByteVector32 = keyManager.channelKeys(localParams.fundingKeyPath).temporaryChannelId
fun temporaryChannelId(channelKeys: ChannelKeys): ByteVector32 = (ByteVector(ByteArray(33) { 0 }) + channelKeys.revocationBasePoint.value).sha256()
}

data class NonInitiator(
Expand Down

Large diffs are not rendered by default.

262 changes: 114 additions & 148 deletions modules/core/src/commonMain/kotlin/fr/acinq/lightning/channel/Helpers.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import fr.acinq.lightning.Lightning.randomBytes32
import fr.acinq.lightning.MilliSatoshi
import fr.acinq.lightning.blockchain.electrum.WalletState
import fr.acinq.lightning.blockchain.fee.FeeratePerKw
import fr.acinq.lightning.crypto.Bolt3Derivation.deriveForCommitment
import fr.acinq.lightning.crypto.ChannelKeys
import fr.acinq.lightning.crypto.KeyManager
import fr.acinq.lightning.crypto.SwapInOnChainKeys
import fr.acinq.lightning.logging.MDCLogger
import fr.acinq.lightning.transactions.*
import fr.acinq.lightning.utils.*
Expand All @@ -29,7 +30,7 @@ import kotlinx.coroutines.CompletableDeferred
sealed class SharedFundingInput {
abstract val info: Transactions.InputInfo
abstract val weight: Int
abstract fun sign(channelKeys: KeyManager.ChannelKeys, tx: Transaction): ByteVector64
abstract fun sign(channelKeys: ChannelKeys, tx: Transaction): ByteVector64

data class Multisig2of2(override val info: Transactions.InputInfo, val fundingTxIndex: Long, val remoteFundingPubkey: PublicKey) : SharedFundingInput() {

Expand All @@ -42,7 +43,7 @@ sealed class SharedFundingInput {
// This value was computed assuming 73 bytes signatures (worst-case scenario).
override val weight: Int = Multisig2of2.weight

override fun sign(channelKeys: KeyManager.ChannelKeys, tx: Transaction): ByteVector64 {
override fun sign(channelKeys: ChannelKeys, tx: Transaction): ByteVector64 {
val fundingKey = channelKeys.fundingKey(fundingTxIndex)
return Transactions.sign(Transactions.TransactionWithInputInfo.SpliceTx(info, tx), fundingKey)
}
Expand Down Expand Up @@ -94,9 +95,9 @@ data class InteractiveTxParams(
// BOLT 2: the initiator's serial IDs MUST use even values and the non-initiator odd values.
val serialIdParity = if (isInitiator) 0 else 1

fun fundingPubkeyScript(channelKeys: KeyManager.ChannelKeys): ByteVector {
fun fundingPubkeyScript(channelKeys: ChannelKeys): ByteVector {
val fundingTxIndex = (sharedInput as? SharedFundingInput.Multisig2of2)?.let { it.fundingTxIndex + 1 } ?: 0
return Helpers.Funding.makeFundingPubKeyScript(channelKeys.fundingPubKey(fundingTxIndex), remoteFundingPubkey)
return Helpers.Funding.makeFundingPubKeyScript(channelKeys.fundingKey(fundingTxIndex).publicKey(), remoteFundingPubkey)
}

fun liquidityFees(purchase: LiquidityAds.Purchase?): MilliSatoshi = purchase?.let { l ->
Expand Down Expand Up @@ -266,8 +267,8 @@ data class FundingContributions(val inputs: List<InteractiveTxInput.Outgoing>, v
* @param walletInputs 2-of-2 swap-in wallet inputs.
*/
fun create(
channelKeys: KeyManager.ChannelKeys,
swapInKeys: KeyManager.SwapInOnChainKeys,
channelKeys: ChannelKeys,
swapInKeys: SwapInOnChainKeys,
params: InteractiveTxParams,
walletInputs: List<WalletState.Utxo>,
liquidityPurchase: LiquidityAds.Purchase?
Expand All @@ -282,8 +283,8 @@ data class FundingContributions(val inputs: List<InteractiveTxInput.Outgoing>, v
* @param changePubKey if provided, a corresponding p2wpkh change output will be created.
*/
fun create(
channelKeys: KeyManager.ChannelKeys,
swapInKeys: KeyManager.SwapInOnChainKeys,
channelKeys: ChannelKeys,
swapInKeys: SwapInOnChainKeys,
params: InteractiveTxParams,
sharedUtxo: Pair<SharedFundingInput, SharedFundingInputBalances>?,
walletInputs: List<WalletState.Utxo>,
Expand Down Expand Up @@ -460,7 +461,8 @@ data class SharedTransaction(

fun sign(session: InteractiveTxSession, keyManager: KeyManager, fundingParams: InteractiveTxParams, localParams: LocalParams, remoteNodeId: PublicKey): PartiallySignedSharedTransaction {
val unsignedTx = buildUnsignedTx()
val sharedSig = fundingParams.sharedInput?.sign(keyManager.channelKeys(localParams.fundingKeyPath), unsignedTx)
val channelKeys = keyManager.channelKeys(localParams.fundingKeyPath)
val sharedSig = fundingParams.sharedInput?.sign(channelKeys, unsignedTx)
// NB: the order in this list must match the order of the transaction's inputs.
val previousOutputs = unsignedTx.txIn.map { spentOutputs[it.outPoint]!! }

Expand Down Expand Up @@ -538,7 +540,7 @@ data class PartiallySignedSharedTransaction(override val tx: SharedTransaction,
override val txId: TxId = localSigs.txId
override val signedTx = null

fun addRemoteSigs(channelKeys: KeyManager.ChannelKeys, fundingParams: InteractiveTxParams, remoteSigs: TxSignatures): FullySignedSharedTransaction? {
fun addRemoteSigs(channelKeys: ChannelKeys, fundingParams: InteractiveTxParams, remoteSigs: TxSignatures): FullySignedSharedTransaction? {
if (localSigs.swapInUserSigs.size != tx.localInputs.filterIsInstance<InteractiveTxInput.LocalLegacySwapIn>().size) return null
if (localSigs.swapInUserPartialSigs.size != tx.localInputs.filterIsInstance<InteractiveTxInput.LocalSwapIn>().size) return null
if (remoteSigs.swapInUserSigs.size != tx.remoteInputs.filterIsInstance<InteractiveTxInput.RemoteLegacySwapIn>().size) return null
Expand All @@ -552,7 +554,7 @@ data class PartiallySignedSharedTransaction(override val tx: SharedTransaction,
is SharedFundingInput.Multisig2of2 -> Scripts.witness2of2(
localSigs.previousFundingTxSig ?: return null,
remoteSigs.previousFundingTxSig ?: return null,
channelKeys.fundingPubKey(it.fundingTxIndex),
channelKeys.fundingKey(it.fundingTxIndex).publicKey(),
it.remoteFundingPubkey,
)
}
Expand Down Expand Up @@ -641,8 +643,8 @@ sealed class InteractiveTxSessionAction {

data class InteractiveTxSession(
val remoteNodeId: PublicKey,
val channelKeys: KeyManager.ChannelKeys,
val swapInKeys: KeyManager.SwapInOnChainKeys,
val channelKeys: ChannelKeys,
val swapInKeys: SwapInOnChainKeys,
val fundingParams: InteractiveTxParams,
val previousFunding: SharedFundingInputBalances,
val toSend: List<Either<InteractiveTxInput.Outgoing, InteractiveTxOutput.Outgoing>>,
Expand Down Expand Up @@ -675,8 +677,8 @@ data class InteractiveTxSession(

constructor(
remoteNodeId: PublicKey,
channelKeys: KeyManager.ChannelKeys,
swapInKeys: KeyManager.SwapInOnChainKeys,
channelKeys: ChannelKeys,
swapInKeys: SwapInOnChainKeys,
fundingParams: InteractiveTxParams,
previousLocalBalance: MilliSatoshi,
previousRemoteBalance: MilliSatoshi,
Expand Down Expand Up @@ -1034,12 +1036,22 @@ data class InteractiveTxSigningSession(
is Either.Right -> localCommit.value.index + 1
}

fun receiveCommitSig(channelKeys: KeyManager.ChannelKeys, channelParams: ChannelParams, remoteCommitSig: CommitSig, currentBlockHeight: Long, logger: MDCLogger): Pair<InteractiveTxSigningSession, InteractiveTxSigningSessionAction> {
fun receiveCommitSig(channelKeys: ChannelKeys, channelParams: ChannelParams, remoteCommitSig: CommitSig, currentBlockHeight: Long, logger: MDCLogger): Pair<InteractiveTxSigningSession, InteractiveTxSigningSessionAction> {
return when (localCommit) {
is Either.Left -> {
val localCommitIndex = localCommit.value.index
val localPerCommitmentPoint = channelKeys.commitmentPoint(localCommitIndex)
when (val signedLocalCommit = LocalCommit.fromCommitSig(channelKeys, channelParams, fundingTxIndex, fundingParams.remoteFundingPubkey, commitInput, remoteCommitSig, localCommitIndex, localCommit.value.spec, localPerCommitmentPoint, logger)) {
val commitKeys = channelKeys.localCommitmentKeys(channelParams, localCommitIndex)
when (val signedLocalCommit = LocalCommit.fromCommitSig(
channelParams = channelParams,
commitKeys = commitKeys,
fundingKey = channelKeys.fundingKey(fundingTxIndex),
remoteFundingPubKey = fundingParams.remoteFundingPubkey,
commitInput = commitInput,
commit = remoteCommitSig,
localCommitIndex = localCommitIndex,
spec = localCommit.value.spec,
log = logger
)) {
is Either.Left -> {
val fundingKey = channelKeys.fundingKey(fundingTxIndex)
val localSigOfLocalTx = Transactions.sign(localCommit.value.commitTx, fundingKey)
Expand Down Expand Up @@ -1067,7 +1079,7 @@ data class InteractiveTxSigningSession(
}
}

fun receiveTxSigs(channelKeys: KeyManager.ChannelKeys, remoteTxSigs: TxSignatures, currentBlockHeight: Long): Either<InteractiveTxSigningSessionAction.AbortFundingAttempt, InteractiveTxSigningSessionAction.SendTxSigs> {
fun receiveTxSigs(channelKeys: ChannelKeys, remoteTxSigs: TxSignatures, currentBlockHeight: Long): Either<InteractiveTxSigningSessionAction.AbortFundingAttempt, InteractiveTxSigningSessionAction.SendTxSigs> {
return when (localCommit) {
is Either.Left -> Either.Left(InteractiveTxSigningSessionAction.AbortFundingAttempt(UnexpectedFundingSignatures(fundingParams.channelId)))
is Either.Right -> when (val fullySignedTx = fundingTx.addRemoteSigs(channelKeys, fundingParams, remoteTxSigs)) {
Expand Down Expand Up @@ -1100,42 +1112,43 @@ data class InteractiveTxSigningSession(
localHtlcs: Set<DirectedHtlc>
): Either<ChannelException, Pair<InteractiveTxSigningSession, CommitSig>> {
val channelKeys = channelParams.localParams.channelKeys(keyManager)
val fundingKey = channelKeys.fundingKey(fundingTxIndex)
val localCommitKeys = channelKeys.localCommitmentKeys(channelParams, localCommitmentIndex)
val remoteCommitKeys = channelKeys.remoteCommitmentKeys(channelParams, remotePerCommitmentPoint)
val unsignedTx = sharedTx.buildUnsignedTx()
val sharedOutputIndex = unsignedTx.txOut.indexOfFirst { it.publicKeyScript == fundingParams.fundingPubkeyScript(channelKeys) }
val liquidityFees = fundingParams.liquidityFees(liquidityPurchase)
return Helpers.Funding.makeCommitTxs(
channelKeys,
channelParams.channelId,
channelParams.localParams, channelParams.remoteParams,
channelParams = channelParams,
fundingAmount = sharedTx.sharedOutput.amount,
toLocal = sharedTx.sharedOutput.localAmount - liquidityFees,
toRemote = sharedTx.sharedOutput.remoteAmount + liquidityFees,
localHtlcs = localHtlcs,
localCommitmentIndex = localCommitmentIndex,
remoteCommitmentIndex = remoteCommitmentIndex,
commitTxFeerate,
fundingTxIndex = fundingTxIndex, fundingTxId = unsignedTx.txid, fundingTxOutputIndex = sharedOutputIndex,
commitTxFeerate = commitTxFeerate,
fundingTxId = unsignedTx.txid,
fundingTxOutputIndex = sharedOutputIndex,
localFundingKey = fundingKey,
remoteFundingPubkey = fundingParams.remoteFundingPubkey,
remotePerCommitmentPoint = remotePerCommitmentPoint
localCommitKeys = localCommitKeys,
remoteCommitKeys = remoteCommitKeys,
).map { firstCommitTx ->
val localSigOfRemoteCommitTx = Transactions.sign(firstCommitTx.remoteCommitTx, channelKeys.fundingKey(fundingTxIndex))
val localSigsOfRemoteHtlcTxs = firstCommitTx.remoteHtlcTxs.map { Transactions.sign(it, channelKeys.htlcKey.deriveForCommitment(remotePerCommitmentPoint), SigHash.SIGHASH_SINGLE or SigHash.SIGHASH_ANYONECANPAY) }

val localSigOfRemoteCommitTx = Transactions.sign(firstCommitTx.remoteCommitTx, fundingKey)
val localSigsOfRemoteHtlcTxs = firstCommitTx.remoteHtlcTxs.map { Transactions.sign(it, remoteCommitKeys.ourHtlcKey, SigHash.SIGHASH_SINGLE or SigHash.SIGHASH_ANYONECANPAY) }
val alternativeSigs = if (firstCommitTx.remoteHtlcTxs.isEmpty()) {
val commitSigTlvs = Commitments.alternativeFeerates.map { feerate ->
val alternativeSpec = firstCommitTx.remoteSpec.copy(feerate = feerate)
val (alternativeRemoteCommitTx, _) = Commitments.makeRemoteTxs(
channelKeys,
remoteCommitmentIndex,
channelParams.localParams,
channelParams.remoteParams,
fundingTxIndex,
fundingParams.remoteFundingPubkey,
firstCommitTx.remoteCommitTx.input,
remotePerCommitmentPoint,
alternativeSpec
channelParams = channelParams,
commitKeys = remoteCommitKeys,
commitTxNumber = remoteCommitmentIndex,
localFundingKey = fundingKey,
remoteFundingPubKey = fundingParams.remoteFundingPubkey,
commitmentInput = firstCommitTx.remoteCommitTx.input,
spec = alternativeSpec
)
val sig = Transactions.sign(alternativeRemoteCommitTx, channelKeys.fundingKey(fundingTxIndex))
val sig = Transactions.sign(alternativeRemoteCommitTx, fundingKey)
CommitSigTlv.AlternativeFeerateSig(feerate, sig)
}
TlvStream(CommitSigTlv.AlternativeFeerateSigs(commitSigTlvs) as CommitSigTlv)
Expand Down
Loading