diff --git a/src/DotNetLightning.Core/Transactions/Transactions.fs b/src/DotNetLightning.Core/Transactions/Transactions.fs
index 1489c31f3..438a624ed 100644
--- a/src/DotNetLightning.Core/Transactions/Transactions.fs
+++ b/src/DotNetLightning.Core/Transactions/Transactions.fs
@@ -777,19 +777,19 @@ module Transactions =
let outputs =
seq {
if toLocalAmount >= dustLimit then
- yield (toLocalAmount, localDestination)
+ yield TxOut(toLocalAmount, localDestination)
if toRemoteAmount >= dustLimit then
- yield (toRemoteAmount, remoteDestination)
+ yield TxOut(toRemoteAmount, remoteDestination)
}
- |> Seq.sortBy (fun (money, dest) -> TxOut(money, dest).ToBytes())
+ |> Seq.sortWith TxOut.LexicographicCompare
let psbt =
let txb = (createTransactionBuilder network)
.AddCoins(commitTxInput)
.SendFees(closingFee)
.SetLockTime(!> 0u)
- for (money, dest) in outputs do
- txb.Send(dest, money) |> ignore
- let tx = txb.BuildTransaction(false)
+ for txOut in outputs do
+ txb.Send(txOut.ScriptPubKey, txOut.Value) |> ignore
+ let tx = txb.BuildTransaction(false)
tx.Version <- 2u
tx.Inputs.[0].Sequence <- !> UINT32_MAX
PSBT.FromTransaction(tx, network)
diff --git a/src/DotNetLightning.Core/Utils/NBitcoinExtensions.fs b/src/DotNetLightning.Core/Utils/NBitcoinExtensions.fs
index 85e4768a7..1d17fd4bc 100644
--- a/src/DotNetLightning.Core/Utils/NBitcoinExtensions.fs
+++ b/src/DotNetLightning.Core/Utils/NBitcoinExtensions.fs
@@ -22,6 +22,32 @@ module NBitcoinExtensions =
res.[31] <- res.[31] ^^^ (uint8 (this.N >>> 0) &&& 0xffuy)
res |> uint256 |> ChannelId
+ type TxOut with
+ static member LexicographicCompare (txOut0: TxOut)
+ (txOut1: TxOut)
+ : int =
+ if txOut0.Value < txOut1.Value then
+ -1
+ elif txOut0.Value > txOut1.Value then
+ 1
+ else
+ let script0 = txOut0.ScriptPubKey.ToBytes()
+ let script1 = txOut1.ScriptPubKey.ToBytes()
+ let rec compare (index: int) =
+ if script0.Length = index && script1.Length = index then
+ 0
+ elif script0.Length = index then
+ -1
+ elif script1.Length = index then
+ 1
+ elif script0.[index] < script1.[index] then
+ -1
+ elif script0.[index] > script1.[index] then
+ 1
+ else
+ compare (index + 1)
+ compare 0
+
type PSBT with
member this.GetMatchingSig(pubkey: PubKey) =
this.Inputs
diff --git a/tests/DotNetLightning.Core.Tests/DotNetLightning.Core.Tests.fsproj b/tests/DotNetLightning.Core.Tests/DotNetLightning.Core.Tests.fsproj
index 2c1776f75..031ab1625 100644
--- a/tests/DotNetLightning.Core.Tests/DotNetLightning.Core.Tests.fsproj
+++ b/tests/DotNetLightning.Core.Tests/DotNetLightning.Core.Tests.fsproj
@@ -23,6 +23,7 @@
+
diff --git a/tests/DotNetLightning.Core.Tests/TxOutLexicographicCompareTests.fs b/tests/DotNetLightning.Core.Tests/TxOutLexicographicCompareTests.fs
new file mode 100644
index 000000000..873c7b19b
--- /dev/null
+++ b/tests/DotNetLightning.Core.Tests/TxOutLexicographicCompareTests.fs
@@ -0,0 +1,86 @@
+module TxOutLexicographicCompareTests
+
+open NBitcoin
+open Expecto
+open DotNetLightning.Utils
+
+[]
+let tests =
+ let test (amount0: int)
+ (amount1: int)
+ (script0: array)
+ (script1: array)
+ (expected: int) =
+ let txOut0 = TxOut(Money amount0, Script script0)
+ let txOut1 = TxOut(Money amount1, Script script1)
+ let result = TxOut.LexicographicCompare txOut0 txOut1
+ Expect.equal
+ result
+ expected
+ (sprintf
+ "unexpected comparison result: \
+ TxOut.LexicographicCompare(TxOut(%i, %A), TxOut(%i, %A))"
+ amount0
+ script0
+ amount1
+ script1
+ )
+
+ testList "TxOutLexicographicCompare tests" [
+ testCase "smaller amount, shorter pubkey, lower bytes values" <| fun _ ->
+ test 1 2 [| 0uy |] [| 1uy; 1uy |] -1
+ testCase "smaller amount, shorter pubkey, equal bytes values" <| fun _ ->
+ test 1 2 [| 0uy |] [| 0uy; 0uy |] -1
+ testCase "smaller amount, shorter pubkey, greater bytes values" <| fun _ ->
+ test 1 2 [| 1uy |] [| 0uy; 0uy |] -1
+ testCase "smaller amount, equal lengths, lower bytes values" <| fun _ ->
+ test 1 2 [| 0uy |] [| 1uy |] -1
+ testCase "smaller amount, equal lengths, equal bytes values" <| fun _ ->
+ test 1 2 [| 0uy |] [| 0uy |] -1
+ testCase "smaller amount, equal lengths, greater bytes values" <| fun _ ->
+ test 1 2 [| 1uy |] [| 0uy |] -1
+ testCase "smaller amount, longer pubkey, lower bytes values" <| fun _ ->
+ test 1 2 [| 0uy; 0uy |] [| 1uy |] -1
+ testCase "smaller amount, longer pubkey, equal bytes values" <| fun _ ->
+ test 1 2 [| 0uy; 0uy |] [| 0uy |] -1
+ testCase "smaller amount, longer pubkey, greater bytes values" <| fun _ ->
+ test 1 2 [| 1uy; 1uy |] [| 0uy |] -1
+
+ testCase "same amount, shorter pubkey, lower bytes values" <| fun _ ->
+ test 1 1 [| 0uy |] [| 1uy; 1uy |] -1
+ testCase "same amount, shorter pubkey, equal bytes values" <| fun _ ->
+ test 1 1 [| 0uy |] [| 0uy; 0uy |] -1
+ testCase "same amount, shorter pubkey, greater bytes values" <| fun _ ->
+ test 1 1 [| 1uy |] [| 0uy; 0uy |] 1
+ testCase "same amount, equal lengths, lower bytes values" <| fun _ ->
+ test 1 1 [| 0uy |] [| 1uy |] -1
+ testCase "same amount, equal lengths, equal bytes values" <| fun _ ->
+ test 1 1 [| 0uy |] [| 0uy |] 0
+ testCase "same amount, equal lengths, greater bytes values" <| fun _ ->
+ test 1 1 [| 1uy |] [| 0uy |] 1
+ testCase "same amount, longer pubkey, lower bytes values" <| fun _ ->
+ test 1 1 [| 0uy; 0uy |] [| 1uy |] -1
+ testCase "same amount, longer pubkey, equal bytes values" <| fun _ ->
+ test 1 1 [| 0uy; 0uy |] [| 0uy |] 1
+ testCase "same amount, longer pubkey, greater bytes values" <| fun _ ->
+ test 1 1 [| 1uy; 1uy |] [| 0uy |] 1
+
+ testCase "greater amount, shorter pubkey, lower bytes values" <| fun _ ->
+ test 2 1 [| 0uy |] [| 1uy; 1uy |] 1
+ testCase "greater amount, shorter pubkey, equal bytes values" <| fun _ ->
+ test 2 1 [| 0uy |] [| 0uy; 0uy |] 1
+ testCase "greater amount, shorter pubkey, greater bytes values" <| fun _ ->
+ test 2 1 [| 1uy |] [| 0uy; 0uy |] 1
+ testCase "greater amount, equal lengths, lower bytes values" <| fun _ ->
+ test 2 1 [| 0uy |] [| 1uy |] 1
+ testCase "greater amount, equal lengths, equal bytes values" <| fun _ ->
+ test 2 1 [| 0uy |] [| 0uy |] 1
+ testCase "greater amount, equal lengths, greater bytes values" <| fun _ ->
+ test 2 1 [| 1uy |] [| 0uy |] 1
+ testCase "greater amount, longer pubkey, lower bytes values" <| fun _ ->
+ test 2 1 [| 0uy; 0uy |] [| 1uy |] 1
+ testCase "greater amount, longer pubkey, equal bytes values" <| fun _ ->
+ test 2 1 [| 0uy; 0uy |] [| 0uy |] 1
+ testCase "greater amount, longer pubkey, greater bytes values" <| fun _ ->
+ test 2 1 [| 1uy; 1uy |] [| 0uy |] 1
+ ]