Skip to content

Commit d6b136d

Browse files
committed
Mono-hop unidirectional payments
1 parent 5c47042 commit d6b136d

File tree

16 files changed

+405
-36
lines changed

16 files changed

+405
-36
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ jobs:
3636
- name: Upload nuget packages (BouncyCastle and native)
3737
run: |
3838
cd $GITHUB_WORKSPACE/src/DotNetLightning.Core
39-
if [ ${{ secrets.NUGET_API_KEY }} ] && [ $GITHUB_REF == "refs/heads/master" ]; then
40-
dotnet nuget push ./bin/Release/DotNetLightning.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
41-
dotnet nuget push ./bin/Release/DotNetLightning.Core.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
39+
if [ ${{ secrets.NUGET_API_KEY }} ]; then
40+
dotnet nuget push ./bin/Release/DotNetLightning.Kiss.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
41+
#dotnet nuget push ./bin/Release/DotNetLightning.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
42+
#dotnet nuget push ./bin/Release/DotNetLightning.Core.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
4243
fi

fsc.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<FscToolPath>/Library/Frameworks/Mono.framework/Versions/Current/Commands</FscToolPath>
1515
<FscToolExe>fsharpc</FscToolExe>
1616
</PropertyGroup>
17-
<PropertyGroup Condition="'$(IsLinux)' == 'true' AND Exists('/usr/bin/fsharpc')">
17+
<PropertyGroup Condition="'$(IsLinux)' == 'true' AND Exists('/usr/bin/fsharpc') AND !Exists('/usr/share/dotnet/dotnet')">
1818
<FscToolPath>/usr/bin</FscToolPath>
1919
<FscToolExe>fsharpc</FscToolExe>
2020
</PropertyGroup>

src/DotNetLightning.Core/Channel/Channel.fs

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ module Channel =
223223
RemoteNextHTLCId = HTLCId.Zero
224224
OriginChannels = Map.empty
225225
// we will receive their next per-commitment point in the next msg, so we temporarily put a random byte array
226-
RemoteNextCommitInfo = DataEncoders.HexEncoder() .DecodeData("0101010101010101010101010101010101010101010101010101010101010101") |> Key |> fun k -> k.PubKey |> Choice2Of2
226+
RemoteNextCommitInfo = DataEncoders.HexEncoder() .DecodeData("0101010101010101010101010101010101010101010101010101010101010101") |> Key |> fun k -> k.PubKey |> RemoteNextCommitInfo.Revoked
227227
RemotePerCommitmentSecrets = ShaChain.Zero
228228
ChannelId =
229229
msg.ChannelId }
@@ -316,7 +316,7 @@ module Channel =
316316
LocalNextHTLCId = HTLCId.Zero
317317
RemoteNextHTLCId = HTLCId.Zero
318318
OriginChannels = Map.empty
319-
RemoteNextCommitInfo = DataEncoders.HexEncoder() .DecodeData("0101010101010101010101010101010101010101010101010101010101010101") |> Key |> fun k -> k.PubKey |> Choice2Of2
319+
RemoteNextCommitInfo = DataEncoders.HexEncoder() .DecodeData("0101010101010101010101010101010101010101010101010101010101010101") |> Key |> fun k -> k.PubKey |> RemoteNextCommitInfo.Revoked
320320
RemotePerCommitmentSecrets = ShaChain.Zero
321321
ChannelId = channelId }
322322
let nextState = { WaitForFundingConfirmedData.Commitments = commitments
@@ -376,7 +376,7 @@ module Channel =
376376
true,
377377
None)
378378
let nextState = { NormalData.Buried = true
379-
Commitments = { state.Commitments with RemoteNextCommitInfo = Choice2Of2(msg.NextPerCommitmentPoint) }
379+
Commitments = { state.Commitments with RemoteNextCommitInfo = RemoteNextCommitInfo.Revoked(msg.NextPerCommitmentPoint) }
380380
ShortChannelId = state.ShortChannelId
381381
ChannelAnnouncement = None
382382
ChannelUpdate = initialChannelUpdate
@@ -388,6 +388,32 @@ module Channel =
388388
[] |> Ok
389389

390390
// ---------- normal operation ---------
391+
| ChannelState.Normal state, MonoHopUnidirectionalPayment cmd when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
392+
sprintf "Could not send mono-hop unidirectional payment %A since shutdown is already in progress." cmd
393+
|> apiMisuse
394+
| ChannelState.Normal state, MonoHopUnidirectionalPayment cmd ->
395+
result {
396+
let payment: MonoHopUnidirectionalPayment = {
397+
ChannelId = state.Commitments.ChannelId
398+
Amount = cmd.Amount
399+
}
400+
let commitments1 = state.Commitments.AddLocalProposal(payment)
401+
402+
let remoteCommit1 =
403+
match commitments1.RemoteNextCommitInfo with
404+
| RemoteNextCommitInfo.WaitingForRevocation info -> info.NextRemoteCommit
405+
| RemoteNextCommitInfo.Revoked _info -> commitments1.RemoteCommit
406+
let! reduced = remoteCommit1.Spec.Reduce(commitments1.RemoteChanges.ACKed, commitments1.LocalChanges.Proposed) |> expectTransactionError
407+
do! Validation.checkOurMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec reduced commitments1 payment
408+
return [ WeAcceptedCMDMonoHopUnidirectionalPayment(payment, commitments1) ]
409+
}
410+
| ChannelState.Normal state, ApplyMonoHopUnidirectionalPayment msg ->
411+
result {
412+
let commitments1 = state.Commitments.AddRemoteProposal(msg)
413+
let! reduced = commitments1.LocalCommit.Spec.Reduce (commitments1.LocalChanges.ACKed, commitments1.RemoteChanges.Proposed) |> expectTransactionError
414+
do! Validation.checkTheirMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec reduced commitments1 msg
415+
return [ WeAcceptedMonoHopUnidirectionalPayment commitments1 ]
416+
}
391417
| ChannelState.Normal state, AddHTLC cmd when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
392418
sprintf "Could not add new HTLC %A since shutdown is already in progress." cmd
393419
|> apiMisuse
@@ -409,8 +435,8 @@ module Channel =
409435
// we need to base the next current commitment on the last sig we sent, even if we didn't yet receive their revocation
410436
let remoteCommit1 =
411437
match commitments1.RemoteNextCommitInfo with
412-
| Choice1Of2 info -> info.NextRemoteCommit
413-
| Choice2Of2 _info -> commitments1.RemoteCommit
438+
| RemoteNextCommitInfo.WaitingForRevocation info -> info.NextRemoteCommit
439+
| RemoteNextCommitInfo.Revoked _info -> commitments1.RemoteCommit
414440
let! reduced = remoteCommit1.Spec.Reduce(commitments1.RemoteChanges.ACKed, commitments1.LocalChanges.Proposed) |> expectTransactionError
415441
do! Validation.checkOurUpdateAddHTLCIsAcceptableWithCurrentSpec reduced commitments1 add
416442
return [ WeAcceptedCMDAddHTLC(add, commitments1) ]
@@ -459,9 +485,9 @@ module Channel =
459485
| _ when (cm.LocalHasChanges() |> not) ->
460486
// Ignore SignCommitment Command (nothing to sign)
461487
return []
462-
| Choice2Of2 _ ->
488+
| RemoteNextCommitInfo.Revoked _ ->
463489
return! cm |> Commitments.sendCommit (cs.Secp256k1Context) (cs.KeysRepository) (cs.Network)
464-
| Choice1Of2 _ ->
490+
| RemoteNextCommitInfo.WaitingForRevocation _ ->
465491
// Already in the process of signing
466492
return []
467493
}
@@ -472,17 +498,17 @@ module Channel =
472498
| ChannelState.Normal state, ApplyRevokeAndACK msg ->
473499
let cm = state.Commitments
474500
match cm.RemoteNextCommitInfo with
475-
| Choice1Of2 _ when (msg.PerCommitmentSecret.ToPubKey() <> cm.RemoteCommit.RemotePerCommitmentPoint) ->
501+
| RemoteNextCommitInfo.WaitingForRevocation _ when (msg.PerCommitmentSecret.ToPubKey() <> cm.RemoteCommit.RemotePerCommitmentPoint) ->
476502
let errorMsg = sprintf "Invalid revoke_and_ack %A; must be %A" msg.PerCommitmentSecret cm.RemoteCommit.RemotePerCommitmentPoint
477503
invalidRevokeAndACK msg errorMsg
478-
| Choice2Of2 _ ->
504+
| RemoteNextCommitInfo.Revoked _ ->
479505
let errorMsg = sprintf "Unexpected revocation"
480506
invalidRevokeAndACK msg errorMsg
481-
| Choice1Of2({ NextRemoteCommit = theirNextCommit }) ->
507+
| RemoteNextCommitInfo.WaitingForRevocation({ NextRemoteCommit = theirNextCommit }) ->
482508
let commitments1 = { cm with LocalChanges = { cm.LocalChanges with Signed = []; ACKed = cm.LocalChanges.ACKed @ cm.LocalChanges.Signed }
483509
RemoteChanges = { cm.RemoteChanges with Signed = [] }
484510
RemoteCommit = theirNextCommit
485-
RemoteNextCommitInfo = Choice2Of2(msg.NextPerCommitmentPoint)
511+
RemoteNextCommitInfo = RemoteNextCommitInfo.Revoked(msg.NextPerCommitmentPoint)
486512
RemotePerCommitmentSecrets = cm.RemotePerCommitmentSecrets.AddHash (msg.PerCommitmentSecret.ToByteArray(), 0xffffffffffffUL - cm.RemoteCommit.Index) }
487513
let result = [ WeAcceptedRevokeAndACK(commitments1) ]
488514
result |> Ok
@@ -528,12 +554,12 @@ module Channel =
528554
// Are we in the middle of a signature?
529555
match cm.RemoteNextCommitInfo with
530556
// yes.
531-
| Choice1Of2 waitingForRevocation ->
557+
| RemoteNextCommitInfo.WaitingForRevocation waitingForRevocation ->
532558
let nextCommitments = { state.Commitments with
533-
RemoteNextCommitInfo = Choice1Of2({ waitingForRevocation with ReSignASAP = true }) }
559+
RemoteNextCommitInfo = RemoteNextCommitInfo.WaitingForRevocation({ waitingForRevocation with ReSignASAP = true }) }
534560
return [ AcceptedShutdownWhileWeHaveUnsignedOutgoingHTLCs(msg, nextCommitments) ]
535561
// No. let's sign right away.
536-
| Choice2Of2 _ ->
562+
| RemoteNextCommitInfo.Revoked _ ->
537563
return [ ChannelStateRequestedSignCommitment; AcceptedShutdownWhileWeHaveUnsignedOutgoingHTLCs(msg, cm) ]
538564
else
539565
let (localShutdown, sendList) = match state.LocalShutdown with
@@ -600,9 +626,9 @@ module Channel =
600626
| _ when (not <| cm.LocalHasChanges()) ->
601627
// nothing to sign
602628
[] |> Ok
603-
| Choice2Of2 _ ->
629+
| RemoteNextCommitInfo.Revoked _ ->
604630
cm |> Commitments.sendCommit (cs.Secp256k1Context) (cs.KeysRepository) (cs.Network)
605-
| Choice1Of2 _waitForRevocation ->
631+
| RemoteNextCommitInfo.WaitingForRevocation _waitForRevocation ->
606632
// Already in the process of signing.
607633
[] |> Ok
608634
| Shutdown state, ApplyCommitmentSigned msg ->
@@ -730,8 +756,12 @@ module Channel =
730756
{ c with State = ChannelState.Normal data }
731757

732758
// ----- normal operation --------
759+
| WeAcceptedCMDMonoHopUnidirectionalPayment(_, newCommitments), ChannelState.Normal d ->
760+
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
733761
| WeAcceptedCMDAddHTLC(_, newCommitments), ChannelState.Normal d ->
734762
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
763+
| WeAcceptedMonoHopUnidirectionalPayment(newCommitments), ChannelState.Normal d ->
764+
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
735765
| WeAcceptedUpdateAddHTLC(newCommitments), ChannelState.Normal d ->
736766
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
737767

src/DotNetLightning.Core/Channel/ChannelCommands.fs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ open NBitcoin
2121
// Y88b d88P Y88b. .d88P 888 " 888 888 " 888 d8888888888 888 Y8888 888 .d88P Y88b d88P
2222
// "Y8888P" "Y88888P" 888 888 888 888 d88P 888 888 Y888 8888888P" "Y8888P"
2323

24+
type CMDMonoHopUnidirectionalPayment = {
25+
Amount: LNMoney
26+
}
27+
2428
type CMDAddHTLC = {
2529
AmountMSat: LNMoney
2630
PaymentHash: PaymentHash
@@ -206,6 +210,8 @@ type ChannelCommand =
206210
| CreateChannelReestablish
207211

208212
// normal
213+
| MonoHopUnidirectionalPayment of CMDMonoHopUnidirectionalPayment
214+
| ApplyMonoHopUnidirectionalPayment of msg: MonoHopUnidirectionalPayment
209215
| AddHTLC of CMDAddHTLC
210216
| ApplyUpdateAddHTLC of msg: UpdateAddHTLC * currentHeight: BlockHeight
211217
| FulfillHTLC of CMDFulfillHTLC
@@ -229,4 +235,4 @@ type ChannelCommand =
229235
// else
230236
| ForceClose
231237
| GetState
232-
| GetStateData
238+
| GetStateData

src/DotNetLightning.Core/Channel/ChannelError.fs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type ChannelError =
3636
// --- case they sent unacceptable msg ---
3737
| InvalidOpenChannel of InvalidOpenChannelError
3838
| InvalidAcceptChannel of InvalidAcceptChannelError
39+
| InvalidMonoHopUnidirectionalPayment of InvalidMonoHopUnidirectionalPaymentError
3940
| InvalidUpdateAddHTLC of InvalidUpdateAddHTLCError
4041
| InvalidRevokeAndACK of InvalidRevokeAndACKError
4142
| InvalidUpdateFee of InvalidUpdateFeeError
@@ -69,6 +70,7 @@ type ChannelError =
6970
| TheyCannotAffordFee (_, _, _) -> Close
7071
| InvalidOpenChannel _ -> DistrustPeer
7172
| InvalidAcceptChannel _ -> DistrustPeer
73+
| InvalidMonoHopUnidirectionalPayment _ -> Close
7274
| InvalidUpdateAddHTLC _ -> Close
7375
| InvalidRevokeAndACK _ -> Close
7476
| InvalidUpdateFee _ -> Close
@@ -145,6 +147,15 @@ and InvalidAcceptChannelError = {
145147
Errors = e
146148
}
147149

150+
and InvalidMonoHopUnidirectionalPaymentError = {
151+
Msg: MonoHopUnidirectionalPayment
152+
Errors: string list
153+
}
154+
with
155+
static member Create msg e = {
156+
Msg = msg
157+
Errors = e
158+
}
148159
and InvalidUpdateAddHTLCError = {
149160
Msg: UpdateAddHTLC
150161
Errors: string list
@@ -458,6 +469,18 @@ module internal AcceptChannelMsgValidation =
458469

459470
(check1 |> Validation.ofResult) *^> check2 *^> check3 *^> check4 *^> check5 *^> check6 *^> check7
460471

472+
module UpdateMonoHopUnidirectionalPaymentWithContext =
473+
let internal checkWeHaveSufficientFunds (state: Commitments) (currentSpec) =
474+
let fees = if (state.LocalParams.IsFunder) then (Transactions.commitTxFee (state.RemoteParams.DustLimitSatoshis) currentSpec) else Money.Zero
475+
let missing = currentSpec.ToRemote.ToMoney() - state.RemoteParams.ChannelReserveSatoshis - fees
476+
if (missing < Money.Zero) then
477+
sprintf "We don't have sufficient funds to send mono-hop unidirectional payment. current to_remote amount is: %A. Remote Channel Reserve is: %A. and fee is %A"
478+
(currentSpec.ToRemote.ToMoney())
479+
(state.RemoteParams.ChannelReserveSatoshis)
480+
(fees)
481+
|> Error
482+
else
483+
Ok()
461484

462485
module UpdateAddHTLCValidation =
463486
let internal checkExpiryIsNotPast (current: BlockHeight) (expiry) =
@@ -472,7 +495,23 @@ module UpdateAddHTLCValidation =
472495
let internal checkAmountIsLargerThanMinimum (htlcMinimum: LNMoney) (amount) =
473496
check (amount) (<) (htlcMinimum) "htlc value (%A) is too small. must be greater or equal to %A"
474497

475-
498+
module internal MonoHopUnidirectionalPaymentValidationWithContext =
499+
let checkWeHaveSufficientFunds (state: Commitments) (currentSpec) =
500+
let fees =
501+
if state.LocalParams.IsFunder then
502+
Transactions.commitTxFee state.RemoteParams.DustLimitSatoshis currentSpec
503+
else
504+
Money.Zero
505+
let missing = currentSpec.ToRemote.ToMoney() - state.RemoteParams.ChannelReserveSatoshis - fees
506+
if (missing < Money.Zero) then
507+
sprintf "We don't have sufficient funds to send mono-hop unidirectional payment. current to_remote amount is: %A. Remote Channel Reserve is: %A. and fee is %A"
508+
(currentSpec.ToRemote.ToMoney())
509+
(state.RemoteParams.ChannelReserveSatoshis)
510+
(fees)
511+
|> Error
512+
else
513+
Ok()
514+
476515
module internal UpdateAddHTLCValidationWithContext =
477516
let checkLessThanHTLCValueInFlightLimit (currentSpec: CommitmentSpec) (limit) (add: UpdateAddHTLC) =
478517
let htlcValueInFlight = currentSpec.HTLCs |> Map.toSeq |> Seq.sumBy (fun (_, v) -> v.Add.AmountMSat)

src/DotNetLightning.Core/Channel/ChannelTypes.fs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ type ChannelEvent =
264264
| BothFundingLocked of nextState: Data.NormalData
265265

266266
// -------- normal operation ------
267+
| WeAcceptedCMDMonoHopUnidirectionalPayment of msg: MonoHopUnidirectionalPayment * newCommitments: Commitments
268+
| WeAcceptedMonoHopUnidirectionalPayment of newCommitments: Commitments
269+
267270
| WeAcceptedCMDAddHTLC of msg: UpdateAddHTLC * newCommitments: Commitments
268271
| WeAcceptedUpdateAddHTLC of newCommitments: Commitments
269272

@@ -355,6 +358,26 @@ type ChannelState =
355358
(fun v cc -> match cc with
356359
| Normal _ -> Normal v
357360
| _ -> cc )
361+
member this.ChannelId: Option<ChannelId> =
362+
match this with
363+
| WaitForInitInternal
364+
| WaitForOpenChannel _
365+
| WaitForAcceptChannel _
366+
| WaitForFundingCreated _ -> None
367+
| WaitForFundingSigned data -> Some data.ChannelId
368+
| WaitForFundingConfirmed data -> Some data.ChannelId
369+
| WaitForFundingLocked data -> Some data.ChannelId
370+
| Normal data -> Some data.ChannelId
371+
| Shutdown data -> Some data.ChannelId
372+
| Negotiating data -> Some data.ChannelId
373+
| Closing data -> Some data.ChannelId
374+
| Closed _
375+
| Offline _
376+
| Syncing _
377+
| ErrFundingLost _
378+
| ErrFundingTimeOut _
379+
| ErrInformationLeak _ -> None
380+
358381
member this.Phase =
359382
match this with
360383
| WaitForInitInternal

src/DotNetLightning.Core/Channel/ChannelValidation.fs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,13 @@ module internal Validation =
169169
*> AcceptChannelMsgValidation.checkConfigPermits conf.PeerChannelConfigLimits msg
170170
|> Result.mapError(InvalidAcceptChannelError.Create msg >> InvalidAcceptChannel)
171171

172+
let checkOurMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: MonoHopUnidirectionalPayment) =
173+
Validation.ofResult(MonoHopUnidirectionalPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
174+
|> Result.mapError(InvalidMonoHopUnidirectionalPaymentError.Create payment >> InvalidMonoHopUnidirectionalPayment)
175+
176+
let checkTheirMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: MonoHopUnidirectionalPayment) =
177+
Validation.ofResult(MonoHopUnidirectionalPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
178+
|> Result.mapError(InvalidMonoHopUnidirectionalPaymentError.Create payment >> InvalidMonoHopUnidirectionalPayment)
172179

173180
let checkCMDAddHTLC (state: NormalData) (cmd: CMDAddHTLC) =
174181
Validation.ofResult(UpdateAddHTLCValidation.checkExpiryIsNotPast cmd.CurrentHeight cmd.Expiry)

0 commit comments

Comments
 (0)