@@ -414,3 +414,179 @@ module internal Commitments =
414414 OriginChannels = originChannels1 }
415415 return [ WeAcceptedCommitmentSigned( nextMsg, nextCommitments) ;]
416416 }
417+
418+ module RemoteForceClose =
419+ // The lightning spec specifies that commitment txs use version 2 bitcoin transactions.
420+ let TxVersionNumberOfCommitmentTxs = 2 u
421+
422+ let check ( thing : bool ): Option < unit > =
423+ if thing then
424+ Some ()
425+ else
426+ None
427+
428+ let tryGetObscuredCommitmentNumber ( fundingOutPoint : OutPoint )
429+ ( transaction : Transaction )
430+ : Option < ObscuredCommitmentNumber > = option {
431+ do ! check ( transaction.Version = TxVersionNumberOfCommitmentTxs)
432+ let! txIn = Seq.tryExactlyOne transaction.Inputs
433+ do ! check ( fundingOutPoint = txIn.PrevOut)
434+ let! obscuredCommitmentNumber =
435+ ObscuredCommitmentNumber.TryFromLockTimeAndSequence transaction.LockTime txIn.Sequence
436+ return obscuredCommitmentNumber
437+ }
438+
439+ let tryGetFundsFromRemoteCommitmentTx ( commitments : Commitments )
440+ ( localChannelPrivKeys : ChannelPrivKeys )
441+ ( network : Network )
442+ ( transaction : Transaction )
443+ : Option < TransactionBuilder > = option {
444+ let! obscuredCommitmentNumber =
445+ tryGetObscuredCommitmentNumber
446+ commitments.FundingScriptCoin.Outpoint
447+ transaction
448+ let localChannelPubKeys = commitments.LocalParams.ChannelPubKeys
449+ let remoteChannelPubKeys = commitments.RemoteParams.ChannelPubKeys
450+ let commitmentNumber =
451+ obscuredCommitmentNumber.Unobscure
452+ false
453+ localChannelPubKeys.PaymentBasepoint
454+ remoteChannelPubKeys.PaymentBasepoint
455+ let perCommitmentSecretOpt =
456+ commitments.RemotePerCommitmentSecrets.GetPerCommitmentSecret commitmentNumber
457+ let! perCommitmentPoint =
458+ match perCommitmentSecretOpt with
459+ | Some perCommitmentSecret -> Some <| perCommitmentSecret.PerCommitmentPoint()
460+ | None ->
461+ if commitments.RemoteCommit.Index = commitmentNumber then
462+ Some commitments.RemoteCommit.RemotePerCommitmentPoint
463+ else
464+ None
465+
466+ let localCommitmentPubKeys =
467+ perCommitmentPoint.DeriveCommitmentPubKeys localChannelPubKeys
468+ let remoteCommitmentPubKeys =
469+ perCommitmentPoint.DeriveCommitmentPubKeys remoteChannelPubKeys
470+
471+ let transactionBuilder = network.CreateTransactionBuilder()
472+
473+ let toRemoteScriptPubKey =
474+ localCommitmentPubKeys.PaymentPubKey.RawPubKey() .WitHash.ScriptPubKey
475+ let toRemoteIndexOpt =
476+ Seq.tryFindIndex
477+ ( fun ( txOut : TxOut ) -> txOut.ScriptPubKey = toRemoteScriptPubKey)
478+ transaction.Outputs
479+ let spentToRemoteOutput =
480+ match toRemoteIndexOpt with
481+ | None -> false
482+ | Some toRemoteIndex ->
483+ let localPaymentPrivKey =
484+ perCommitmentPoint.DerivePaymentPrivKey
485+ localChannelPrivKeys.PaymentBasepointSecret
486+ ( transactionBuilder.AddKeys ( localPaymentPrivKey.RawKey()))
487+ .AddCoins ( Coin( transaction, uint32 toRemoteIndex))
488+ |> ignore
489+ true
490+
491+ let spentToLocalOutput =
492+ match perCommitmentSecretOpt with
493+ | None -> false
494+ | Some perCommitmentSecret ->
495+ let toLocalScriptPubKey =
496+ Scripts.toLocalDelayed
497+ localCommitmentPubKeys.RevocationPubKey
498+ commitments.RemoteParams.ToSelfDelay
499+ remoteCommitmentPubKeys.DelayedPaymentPubKey
500+ let toLocalIndexOpt =
501+ let toLocalWitScriptPubKey = toLocalScriptPubKey.WitHash.ScriptPubKey
502+ Seq.tryFindIndex
503+ ( fun ( txOut : TxOut ) -> txOut.ScriptPubKey = toLocalWitScriptPubKey)
504+ transaction.Outputs
505+ match toLocalIndexOpt with
506+ | None -> false
507+ | Some toLocalIndex ->
508+ let revocationPrivKey =
509+ perCommitmentSecret.DeriveRevocationPrivKey
510+ localChannelPrivKeys.RevocationBasepointSecret
511+ transactionBuilder.Extensions.Add ( CommitmentToLocalExtension())
512+ ( transactionBuilder.AddKeys ( revocationPrivKey.RawKey()))
513+ .AddCoins
514+ ( ScriptCoin( transaction, uint32 toLocalIndex, toLocalScriptPubKey))
515+ |> ignore
516+ true
517+
518+ do ! check ( spentToRemoteOutput || spentToLocalOutput)
519+
520+ return transactionBuilder
521+ }
522+
523+ let tryGetFundsFromLocalCommitmentTx ( commitments : Commitments )
524+ ( localChannelPrivKeys : ChannelPrivKeys )
525+ ( network : Network )
526+ ( transaction : Transaction )
527+ : Option < TransactionBuilder > = option {
528+ let! obscuredCommitmentNumber =
529+ tryGetObscuredCommitmentNumber
530+ commitments.FundingScriptCoin.Outpoint
531+ transaction
532+ let localChannelPubKeys = commitments.LocalParams.ChannelPubKeys
533+ let remoteChannelPubKeys = commitments.RemoteParams.ChannelPubKeys
534+ let commitmentNumber =
535+ obscuredCommitmentNumber.Unobscure
536+ true
537+ localChannelPubKeys.PaymentBasepoint
538+ remoteChannelPubKeys.PaymentBasepoint
539+
540+ let perCommitmentPoint =
541+ localChannelPrivKeys.CommitmentSeed.DerivePerCommitmentPoint commitmentNumber
542+ let localCommitmentPubKeys =
543+ perCommitmentPoint.DeriveCommitmentPubKeys localChannelPubKeys
544+ let remoteCommitmentPubKeys =
545+ perCommitmentPoint.DeriveCommitmentPubKeys remoteChannelPubKeys
546+
547+ let transactionBuilder = network.CreateTransactionBuilder()
548+
549+ let toLocalScriptPubKey =
550+ Scripts.toLocalDelayed
551+ remoteCommitmentPubKeys.RevocationPubKey
552+ commitments.LocalParams.ToSelfDelay
553+ localCommitmentPubKeys.DelayedPaymentPubKey
554+ let! toLocalIndex =
555+ let toLocalWitScriptPubKey = toLocalScriptPubKey.WitHash.ScriptPubKey
556+ Seq.tryFindIndex
557+ ( fun ( txOut : TxOut ) -> txOut.ScriptPubKey = toLocalWitScriptPubKey)
558+ transaction.Outputs
559+
560+ let delayedPaymentPrivKey =
561+ perCommitmentPoint.DeriveDelayedPaymentPrivKey
562+ localChannelPrivKeys.DelayedPaymentBasepointSecret
563+ transactionBuilder.Extensions.Add ( CommitmentToLocalExtension())
564+ ( transactionBuilder.AddKeys ( delayedPaymentPrivKey.RawKey()))
565+ .AddCoins
566+ ( ScriptCoin( transaction, uint32 toLocalIndex, toLocalScriptPubKey))
567+ |> ignore
568+
569+ return transactionBuilder
570+ }
571+
572+ let getFundsFromForceClosingTransaction ( commitments : Commitments )
573+ ( localChannelPrivKeys : ChannelPrivKeys )
574+ ( network : Network )
575+ ( transaction : Transaction )
576+ : Option < TransactionBuilder > =
577+ let attemptsSeq = seq {
578+ yield
579+ tryGetFundsFromRemoteCommitmentTx
580+ commitments
581+ localChannelPrivKeys
582+ network
583+ transaction
584+ yield
585+ tryGetFundsFromLocalCommitmentTx
586+ commitments
587+ localChannelPrivKeys
588+ network
589+ transaction
590+ }
591+ Seq.tryPick ( fun opt -> opt) attemptsSeq
592+
0 commit comments