@@ -589,20 +589,16 @@ object Transactions {
589
589
} match {
590
590
case Some (outputIndex) =>
591
591
val input = InputInfo (OutPoint (commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
592
- // unsigned tx
593
- val tx = Transaction (
592
+ val unsignedTx = Transaction (
594
593
version = 2 ,
595
594
txIn = TxIn (input.outPoint, ByteVector .empty, getHtlcTxInputSequence(commitmentFormat)) :: Nil ,
596
- txOut = TxOut (Satoshi (0 ), localFinalScriptPubKey) :: Nil ,
597
- lockTime = 0 )
598
- val weight = addSigs(ClaimHtlcSuccessTx (input, tx, htlc.paymentHash, htlc.id, ConfirmationTarget .Absolute (BlockHeight (htlc.cltvExpiry.toLong))), PlaceHolderSig , ByteVector32 .Zeroes ).tx.weight()
599
- val fee = weight2fee(feeratePerKw, weight)
600
- val amount = input.txOut.amount - fee
601
- if (amount < dustLimit) {
602
- Left (AmountBelowDustLimit )
603
- } else {
604
- val tx1 = tx.copy(txOut = tx.txOut.head.copy(amount = amount) :: Nil )
605
- Right (ClaimHtlcSuccessTx (input, tx1, htlc.paymentHash, htlc.id, ConfirmationTarget .Absolute (BlockHeight (htlc.cltvExpiry.toLong))))
595
+ txOut = TxOut (0 sat, localFinalScriptPubKey) :: Nil ,
596
+ lockTime = 0
597
+ )
598
+ val dummySignedTx = addSigs(ClaimHtlcSuccessTx (input, unsignedTx, htlc.paymentHash, htlc.id, ConfirmationTarget .Absolute (BlockHeight (htlc.cltvExpiry.toLong))), PlaceHolderSig , ByteVector32 .Zeroes )
599
+ skipTxIfBelowDust(dummySignedTx, feeratePerKw, dustLimit).map { amount =>
600
+ val tx = unsignedTx.copy(txOut = TxOut (amount, localFinalScriptPubKey) :: Nil )
601
+ ClaimHtlcSuccessTx (input, tx, htlc.paymentHash, htlc.id, ConfirmationTarget .Absolute (BlockHeight (htlc.cltvExpiry.toLong)))
606
602
}
607
603
case None => Left (OutputNotFound )
608
604
}
@@ -622,20 +618,16 @@ object Transactions {
622
618
} match {
623
619
case Some (outputIndex) =>
624
620
val input = InputInfo (OutPoint (commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
625
- // unsigned tx
626
- val tx = Transaction (
621
+ val unsignedTx = Transaction (
627
622
version = 2 ,
628
623
txIn = TxIn (input.outPoint, ByteVector .empty, getHtlcTxInputSequence(commitmentFormat)) :: Nil ,
629
- txOut = TxOut (Satoshi (0 ), localFinalScriptPubKey) :: Nil ,
630
- lockTime = htlc.cltvExpiry.toLong)
631
- val weight = addSigs(ClaimHtlcTimeoutTx (input, tx, htlc.id, ConfirmationTarget .Absolute (BlockHeight (htlc.cltvExpiry.toLong))), PlaceHolderSig ).tx.weight()
632
- val fee = weight2fee(feeratePerKw, weight)
633
- val amount = input.txOut.amount - fee
634
- if (amount < dustLimit) {
635
- Left (AmountBelowDustLimit )
636
- } else {
637
- val tx1 = tx.copy(txOut = tx.txOut.head.copy(amount = amount) :: Nil )
638
- Right (ClaimHtlcTimeoutTx (input, tx1, htlc.id, ConfirmationTarget .Absolute (BlockHeight (htlc.cltvExpiry.toLong))))
624
+ txOut = TxOut (0 sat, localFinalScriptPubKey) :: Nil ,
625
+ lockTime = htlc.cltvExpiry.toLong
626
+ )
627
+ val dummySignedTx = addSigs(ClaimHtlcTimeoutTx (input, unsignedTx, htlc.id, ConfirmationTarget .Absolute (BlockHeight (htlc.cltvExpiry.toLong))), PlaceHolderSig )
628
+ skipTxIfBelowDust(dummySignedTx, feeratePerKw, dustLimit).map { amount =>
629
+ val tx = unsignedTx.copy(txOut = TxOut (amount, localFinalScriptPubKey) :: Nil )
630
+ ClaimHtlcTimeoutTx (input, tx, htlc.id, ConfirmationTarget .Absolute (BlockHeight (htlc.cltvExpiry.toLong)))
639
631
}
640
632
case None => Left (OutputNotFound )
641
633
}
@@ -648,21 +640,16 @@ object Transactions {
648
640
case Left (skip) => Left (skip)
649
641
case Right (outputIndex) =>
650
642
val input = InputInfo (OutPoint (commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
651
- // unsigned tx
652
- val tx = Transaction (
643
+ val unsignedTx = Transaction (
653
644
version = 2 ,
654
- txIn = TxIn (input.outPoint, ByteVector .empty, 0x00000000L) :: Nil ,
655
- txOut = TxOut (Satoshi (0 ), localFinalScriptPubKey) :: Nil ,
656
- lockTime = 0 )
657
- // compute weight with a dummy 73 bytes signature (the largest you can get)
658
- val weight = addSigs(ClaimP2WPKHOutputTx (input, tx), keys, PlaceHolderSig ).tx.weight()
659
- val fee = weight2fee(feeratePerKw, weight)
660
- val amount = input.txOut.amount - fee
661
- if (amount < localDustLimit) {
662
- Left (AmountBelowDustLimit )
663
- } else {
664
- val tx1 = tx.copy(txOut = tx.txOut.head.copy(amount = amount) :: Nil )
665
- Right (ClaimP2WPKHOutputTx (input, tx1))
645
+ txIn = TxIn (input.outPoint, ByteVector .empty, 0 ) :: Nil ,
646
+ txOut = TxOut (0 sat, localFinalScriptPubKey) :: Nil ,
647
+ lockTime = 0
648
+ )
649
+ val dummySignedTx = addSigs(ClaimP2WPKHOutputTx (input, unsignedTx), keys, PlaceHolderSig )
650
+ skipTxIfBelowDust(dummySignedTx, feeratePerKw, localDustLimit).map { amount =>
651
+ val tx = unsignedTx.copy(txOut = TxOut (amount, localFinalScriptPubKey) :: Nil )
652
+ ClaimP2WPKHOutputTx (input, tx)
666
653
}
667
654
}
668
655
}
@@ -674,21 +661,16 @@ object Transactions {
674
661
case Left (skip) => Left (skip)
675
662
case Right (outputIndex) =>
676
663
val input = InputInfo (OutPoint (commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
677
- // unsigned transaction
678
- val tx = Transaction (
664
+ val unsignedTx = Transaction (
679
665
version = 2 ,
680
666
txIn = TxIn (input.outPoint, ByteVector .empty, 1 ) :: Nil ,
681
- txOut = TxOut (Satoshi (0 ), localFinalScriptPubKey) :: Nil ,
682
- lockTime = 0 )
683
- // compute weight with a dummy 73 bytes signature (the largest you can get)
684
- val weight = addSigs(ClaimRemoteDelayedOutputTx (input, tx), PlaceHolderSig ).tx.weight()
685
- val fee = weight2fee(feeratePerKw, weight)
686
- val amount = input.txOut.amount - fee
687
- if (amount < localDustLimit) {
688
- Left (AmountBelowDustLimit )
689
- } else {
690
- val tx1 = tx.copy(txOut = tx.txOut.head.copy(amount = amount) :: Nil )
691
- Right (ClaimRemoteDelayedOutputTx (input, tx1))
667
+ txOut = TxOut (0 sat, localFinalScriptPubKey) :: Nil ,
668
+ lockTime = 0
669
+ )
670
+ val dummySignedTx = addSigs(ClaimRemoteDelayedOutputTx (input, unsignedTx), PlaceHolderSig )
671
+ skipTxIfBelowDust(dummySignedTx, feeratePerKw, localDustLimit).map { amount =>
672
+ val tx = unsignedTx.copy(txOut = TxOut (amount, localFinalScriptPubKey) :: Nil )
673
+ ClaimRemoteDelayedOutputTx (input, tx)
692
674
}
693
675
}
694
676
}
@@ -712,21 +694,16 @@ object Transactions {
712
694
case Left (skip) => Left (skip)
713
695
case Right (outputIndex) =>
714
696
val input = InputInfo (OutPoint (parentTx, outputIndex), parentTx.txOut(outputIndex), write(redeemScript))
715
- // unsigned transaction
716
- val tx = Transaction (
697
+ val unsignedTx = Transaction (
717
698
version = 2 ,
718
699
txIn = TxIn (input.outPoint, ByteVector .empty, toLocalDelay.toInt) :: Nil ,
719
- txOut = TxOut (Satoshi (0 ), localFinalScriptPubKey) :: Nil ,
720
- lockTime = 0 )
721
- // compute weight with a dummy 73 bytes signature (the largest you can get)
722
- val weight = addSigs(ClaimLocalDelayedOutputTx (input, tx), PlaceHolderSig ).tx.weight()
723
- val fee = weight2fee(feeratePerKw, weight)
724
- val amount = input.txOut.amount - fee
725
- if (amount < localDustLimit) {
726
- Left (AmountBelowDustLimit )
727
- } else {
728
- val tx1 = tx.copy(txOut = tx.txOut.head.copy(amount = amount) :: Nil )
729
- Right (input, tx1)
700
+ txOut = TxOut (0 sat, localFinalScriptPubKey) :: Nil ,
701
+ lockTime = 0
702
+ )
703
+ val dummySignedTx = addSigs(ClaimLocalDelayedOutputTx (input, unsignedTx), PlaceHolderSig )
704
+ skipTxIfBelowDust(dummySignedTx, feeratePerKw, localDustLimit).map { amount =>
705
+ val tx = unsignedTx.copy(txOut = TxOut (amount, localFinalScriptPubKey) :: Nil )
706
+ (input, tx)
730
707
}
731
708
}
732
709
}
@@ -738,13 +715,13 @@ object Transactions {
738
715
case Left (skip) => Left (skip)
739
716
case Right (outputIndex) =>
740
717
val input = InputInfo (OutPoint (commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
741
- // unsigned transaction
742
- val tx = Transaction (
718
+ val unsignedTx = Transaction (
743
719
version = 2 ,
744
720
txIn = TxIn (input.outPoint, ByteVector .empty, 0 ) :: Nil ,
745
721
txOut = Nil , // anchor is only used to bump fees, the output will be added later depending on available inputs
746
- lockTime = 0 )
747
- Right (ClaimAnchorOutputTx (input, tx, confirmationTarget))
722
+ lockTime = 0
723
+ )
724
+ Right (ClaimAnchorOutputTx (input, unsignedTx, confirmationTarget))
748
725
}
749
726
}
750
727
@@ -755,21 +732,16 @@ object Transactions {
755
732
case Left (skip) => Seq (Left (skip))
756
733
case Right (outputIndexes) => outputIndexes.map(outputIndex => {
757
734
val input = InputInfo (OutPoint (htlcTx, outputIndex), htlcTx.txOut(outputIndex), write(redeemScript))
758
- // unsigned transaction
759
- val tx = Transaction (
735
+ val unsignedTx = Transaction (
760
736
version = 2 ,
761
737
txIn = TxIn (input.outPoint, ByteVector .empty, 0xffffffffL) :: Nil ,
762
- txOut = TxOut (Satoshi (0 ), localFinalScriptPubKey) :: Nil ,
763
- lockTime = 0 )
764
- // compute weight with a dummy 73 bytes signature (the largest you can get)
765
- val weight = addSigs(ClaimHtlcDelayedOutputPenaltyTx (input, tx), PlaceHolderSig ).tx.weight()
766
- val fee = weight2fee(feeratePerKw, weight)
767
- val amount = input.txOut.amount - fee
768
- if (amount < localDustLimit) {
769
- Left (AmountBelowDustLimit )
770
- } else {
771
- val tx1 = tx.copy(txOut = tx.txOut.head.copy(amount = amount) :: Nil )
772
- Right (ClaimHtlcDelayedOutputPenaltyTx (input, tx1))
738
+ txOut = TxOut (0 sat, localFinalScriptPubKey) :: Nil ,
739
+ lockTime = 0
740
+ )
741
+ val dummySignedTx = addSigs(ClaimHtlcDelayedOutputPenaltyTx (input, unsignedTx), PlaceHolderSig )
742
+ skipTxIfBelowDust(dummySignedTx, feeratePerKw, localDustLimit).map { amount =>
743
+ val tx = unsignedTx.copy(txOut = TxOut (amount, localFinalScriptPubKey) :: Nil )
744
+ ClaimHtlcDelayedOutputPenaltyTx (input, tx)
773
745
}
774
746
})
775
747
}
@@ -782,42 +754,32 @@ object Transactions {
782
754
case Left (skip) => Left (skip)
783
755
case Right (outputIndex) =>
784
756
val input = InputInfo (OutPoint (commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
785
- // unsigned transaction
786
- val tx = Transaction (
757
+ val unsignedTx = Transaction (
787
758
version = 2 ,
788
759
txIn = TxIn (input.outPoint, ByteVector .empty, 0xffffffffL) :: Nil ,
789
- txOut = TxOut (Satoshi (0 ), localFinalScriptPubKey) :: Nil ,
790
- lockTime = 0 )
791
- // compute weight with a dummy 73 bytes signature (the largest you can get)
792
- val weight = addSigs(MainPenaltyTx (input, tx), PlaceHolderSig ).tx.weight()
793
- val fee = weight2fee(feeratePerKw, weight)
794
- val amount = input.txOut.amount - fee
795
- if (amount < localDustLimit) {
796
- Left (AmountBelowDustLimit )
797
- } else {
798
- val tx1 = tx.copy(txOut = tx.txOut.head.copy(amount = amount) :: Nil )
799
- Right (MainPenaltyTx (input, tx1))
760
+ txOut = TxOut (0 sat, localFinalScriptPubKey) :: Nil ,
761
+ lockTime = 0
762
+ )
763
+ val dummySignedTx = addSigs(MainPenaltyTx (input, unsignedTx), PlaceHolderSig )
764
+ skipTxIfBelowDust(dummySignedTx, feeratePerKw, localDustLimit).map { amount =>
765
+ val tx = unsignedTx.copy(txOut = TxOut (amount, localFinalScriptPubKey) :: Nil )
766
+ MainPenaltyTx (input, tx)
800
767
}
801
768
}
802
769
}
803
770
804
771
def makeHtlcPenaltyTx (commitTx : Transaction , htlcOutputIndex : Int , redeemScript : ByteVector , localDustLimit : Satoshi , localFinalScriptPubKey : ByteVector , feeratePerKw : FeeratePerKw ): Either [TxGenerationSkipped , HtlcPenaltyTx ] = {
805
772
val input = InputInfo (OutPoint (commitTx, htlcOutputIndex), commitTx.txOut(htlcOutputIndex), redeemScript)
806
- // unsigned transaction
807
- val tx = Transaction (
773
+ val unsignedTx = Transaction (
808
774
version = 2 ,
809
775
txIn = TxIn (input.outPoint, ByteVector .empty, 0xffffffffL) :: Nil ,
810
- txOut = TxOut (Satoshi (0 ), localFinalScriptPubKey) :: Nil ,
811
- lockTime = 0 )
812
- // compute weight with a dummy 73 bytes signature (the largest you can get)
813
- val weight = addSigs(MainPenaltyTx (input, tx), PlaceHolderSig ).tx.weight()
814
- val fee = weight2fee(feeratePerKw, weight)
815
- val amount = input.txOut.amount - fee
816
- if (amount < localDustLimit) {
817
- Left (AmountBelowDustLimit )
818
- } else {
819
- val tx1 = tx.copy(txOut = tx.txOut.head.copy(amount = amount) :: Nil )
820
- Right (HtlcPenaltyTx (input, tx1))
776
+ txOut = TxOut (0 sat, localFinalScriptPubKey) :: Nil ,
777
+ lockTime = 0
778
+ )
779
+ val dummySignedTx = addSigs(MainPenaltyTx (input, unsignedTx), PlaceHolderSig )
780
+ skipTxIfBelowDust(dummySignedTx, feeratePerKw, localDustLimit).map { amount =>
781
+ val tx = unsignedTx.copy(txOut = TxOut (amount, localFinalScriptPubKey) :: Nil )
782
+ HtlcPenaltyTx (input, tx)
821
783
}
822
784
}
823
785
@@ -913,6 +875,22 @@ object Transactions {
913
875
}
914
876
}
915
877
878
+ /**
879
+ * We skip creating transactions spending commitment outputs when the remaining amount is below dust.
880
+ *
881
+ * @param dummySignedTx the transaction with a witness filled with dummy signatures (to compute its weight).
882
+ * @return the output amount, unless the transaction should be skipped because it's below dust.
883
+ */
884
+ private def skipTxIfBelowDust (dummySignedTx : TransactionWithInputInfo , feerate : FeeratePerKw , dustLimit : Satoshi ): Either [TxGenerationSkipped , Satoshi ] = {
885
+ val fee = weight2fee(feerate, dummySignedTx.tx.weight())
886
+ val amount = dummySignedTx.input.txOut.amount - fee
887
+ if (amount < dustLimit) {
888
+ Left (AmountBelowDustLimit )
889
+ } else {
890
+ Right (amount)
891
+ }
892
+ }
893
+
916
894
def findPubKeyScriptIndex (tx : Transaction , pubkeyScript : ByteVector ): Either [TxGenerationSkipped , Int ] = {
917
895
val outputIndex = tx.txOut.indexWhere(_.publicKeyScript == pubkeyScript)
918
896
if (outputIndex >= 0 ) {
0 commit comments