Skip to content

Commit ee56733

Browse files
canndrewknocte
authored andcommitted
Mono-hop unidirectional payments
1 parent 617f973 commit ee56733

File tree

16 files changed

+339
-16
lines changed

16 files changed

+339
-16
lines changed

.github/workflows/publish_master.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,5 @@ jobs:
3333
if: startsWith(matrix.os, 'ubuntu')
3434
run: |
3535
dotnet pack ./src/DotNetLightning.Core -p:Configuration=Release --version-suffix date`date +%Y%m%d-%H%M`-git-`echo $GITHUB_SHA | head -c 7` -p:BouncyCastle=True
36-
dotnet nuget push ./src/DotNetLightning.Core/bin/Release/DotNetLightning.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
36+
dotnet nuget push ./src/DotNetLightning.Core/bin/Release/DotNetLightning.Kiss.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
3737
38-
- name: Upload nuget packages (native)
39-
run: |
40-
bash -c "dotnet pack ./src/DotNetLightning.Core -p:Configuration=Release --version-suffix date$(date +%Y%m%d-%H%M)-git-$(echo $GITHUB_SHA | head -c 7)-${{ matrix.RID }}"
41-
bash -c "dotnet nuget push ./src/DotNetLightning.Core/bin/Release/DotNetLightning.Core.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json"

src/DotNetLightning.Core/Channel/Channel.fs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,32 @@ module Channel =
409409
[] |> Ok
410410

411411
// ---------- normal operation ---------
412+
| ChannelState.Normal state, MonoHopUnidirectionalPayment op when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
413+
sprintf "Could not send mono-hop unidirectional payment %A since shutdown is already in progress." op
414+
|> apiMisuse
415+
| ChannelState.Normal state, MonoHopUnidirectionalPayment op ->
416+
result {
417+
let payment: MonoHopUnidirectionalPaymentMsg = {
418+
ChannelId = state.Commitments.ChannelId
419+
Amount = op.Amount
420+
}
421+
let commitments1 = state.Commitments.AddLocalProposal(payment)
422+
423+
let remoteCommit1 =
424+
match commitments1.RemoteNextCommitInfo with
425+
| RemoteNextCommitInfo.Waiting info -> info.NextRemoteCommit
426+
| RemoteNextCommitInfo.Revoked _info -> commitments1.RemoteCommit
427+
let! reduced = remoteCommit1.Spec.Reduce(commitments1.RemoteChanges.ACKed, commitments1.LocalChanges.Proposed) |> expectTransactionError
428+
do! Validation.checkOurMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec reduced commitments1 payment
429+
return [ WeAcceptedOperationMonoHopUnidirectionalPayment(payment, commitments1) ]
430+
}
431+
| ChannelState.Normal state, ApplyMonoHopUnidirectionalPayment msg ->
432+
result {
433+
let commitments1 = state.Commitments.AddRemoteProposal(msg)
434+
let! reduced = commitments1.LocalCommit.Spec.Reduce (commitments1.LocalChanges.ACKed, commitments1.RemoteChanges.Proposed) |> expectTransactionError
435+
do! Validation.checkTheirMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec reduced commitments1 msg
436+
return [ WeAcceptedMonoHopUnidirectionalPayment commitments1 ]
437+
}
412438
| ChannelState.Normal state, AddHTLC op when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
413439
sprintf "Could not add new HTLC %A since shutdown is already in progress." op
414440
|> apiMisuse
@@ -514,8 +540,8 @@ module Channel =
514540
RemoteCommit = theirNextCommit
515541
RemoteNextCommitInfo = RemoteNextCommitInfo.Revoked msg.NextPerCommitmentPoint
516542
RemotePerCommitmentSecrets = remotePerCommitmentSecrets }
517-
let result = Ok [ WeAcceptedRevokeAndACK(commitments1) ]
518-
failwith "needs update"
543+
Console.WriteLine("WARNING: revocation is not implemented yet")
544+
Ok [ WeAcceptedRevokeAndACK(commitments1) ]
519545

520546
| ChannelState.Normal state, ChannelCommand.Close cmd ->
521547
let localSPK = cmd.ScriptPubKey |> Option.defaultValue (state.Commitments.LocalParams.DefaultFinalScriptPubKey)
@@ -762,8 +788,12 @@ module Channel =
762788
{ c with State = ChannelState.Normal data }
763789

764790
// ----- normal operation --------
791+
| WeAcceptedOperationMonoHopUnidirectionalPayment(_, newCommitments), ChannelState.Normal normalData ->
792+
{ c with State = ChannelState.Normal({ normalData with Commitments = newCommitments }) }
765793
| WeAcceptedOperationAddHTLC(_, newCommitments), ChannelState.Normal d ->
766794
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
795+
| WeAcceptedMonoHopUnidirectionalPayment(newCommitments), ChannelState.Normal normalData ->
796+
{ c with State = ChannelState.Normal({ normalData with Commitments = newCommitments }) }
767797
| WeAcceptedUpdateAddHTLC(newCommitments), ChannelState.Normal d ->
768798
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
769799

src/DotNetLightning.Core/Channel/ChannelError.fs

Lines changed: 47 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
@@ -179,6 +181,18 @@ and InvalidAcceptChannelError = {
179181
member this.Message =
180182
String.concat "; " this.Errors
181183

184+
and InvalidMonoHopUnidirectionalPaymentError = {
185+
NetworkMsg: MonoHopUnidirectionalPaymentMsg
186+
Errors: string list
187+
}
188+
with
189+
static member Create msg e = {
190+
NetworkMsg = msg
191+
Errors = e
192+
}
193+
member this.Message =
194+
String.concat "; " this.Errors
195+
182196
and InvalidUpdateAddHTLCError = {
183197
NetworkMsg: UpdateAddHTLCMsg
184198
Errors: string list
@@ -507,6 +521,22 @@ module internal AcceptChannelMsgValidation =
507521

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

524+
module UpdateMonoHopUnidirectionalPaymentWithContext =
525+
let internal checkWeHaveSufficientFunds (state: Commitments) (currentSpec) =
526+
let fees =
527+
if state.LocalParams.IsFunder then
528+
Transactions.commitTxFee state.RemoteParams.DustLimitSatoshis currentSpec
529+
else
530+
Money.Zero
531+
let missing = currentSpec.ToRemote.ToMoney() - state.RemoteParams.ChannelReserveSatoshis - fees
532+
if missing < Money.Zero then
533+
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"
534+
(currentSpec.ToRemote.ToMoney())
535+
state.RemoteParams.ChannelReserveSatoshis
536+
fees
537+
|> Error
538+
else
539+
Ok()
510540

511541
module UpdateAddHTLCValidation =
512542
let internal checkExpiryIsNotPast (current: BlockHeight) (expiry) =
@@ -521,7 +551,23 @@ module UpdateAddHTLCValidation =
521551
let internal checkAmountIsLargerThanMinimum (htlcMinimum: LNMoney) (amount) =
522552
check (amount) (<) (htlcMinimum) "htlc value (%A) is too small. must be greater or equal to %A"
523553

524-
554+
module internal MonoHopUnidirectionalPaymentValidationWithContext =
555+
let checkWeHaveSufficientFunds (state: Commitments) (currentSpec) =
556+
let fees =
557+
if state.LocalParams.IsFunder then
558+
Transactions.commitTxFee state.RemoteParams.DustLimitSatoshis currentSpec
559+
else
560+
Money.Zero
561+
let missing = currentSpec.ToRemote.ToMoney() - state.RemoteParams.ChannelReserveSatoshis - fees
562+
if missing < Money.Zero then
563+
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"
564+
(currentSpec.ToRemote.ToMoney())
565+
state.RemoteParams.ChannelReserveSatoshis
566+
fees
567+
|> Error
568+
else
569+
Ok()
570+
525571
module internal UpdateAddHTLCValidationWithContext =
526572
let checkLessThanHTLCValueInFlightLimit (currentSpec: CommitmentSpec) (limit) (add: UpdateAddHTLCMsg) =
527573
let htlcValueInFlight = currentSpec.HTLCs |> Map.toSeq |> Seq.sumBy (fun (_, v) -> v.Add.Amount)

src/DotNetLightning.Core/Channel/ChannelOperations.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ open DotNetLightning.Serialize
1212

1313
open NBitcoin
1414

15+
type OperationMonoHopUnidirectionalPayment = {
16+
Amount: LNMoney
17+
}
18+
1519
type OperationAddHTLC = {
1620
Amount: LNMoney
1721
PaymentHash: PaymentHash
@@ -197,6 +201,8 @@ type ChannelCommand =
197201
| CreateChannelReestablish
198202

199203
// normal
204+
| MonoHopUnidirectionalPayment of OperationMonoHopUnidirectionalPayment
205+
| ApplyMonoHopUnidirectionalPayment of msg: MonoHopUnidirectionalPaymentMsg
200206
| AddHTLC of OperationAddHTLC
201207
| ApplyUpdateAddHTLC of msg: UpdateAddHTLCMsg * currentHeight: BlockHeight
202208
| FulfillHTLC of OperationFulfillHTLC

src/DotNetLightning.Core/Channel/ChannelTypes.fs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ type ChannelEvent =
267267
| BothFundingLocked of nextState: Data.NormalData
268268

269269
// -------- normal operation ------
270+
| WeAcceptedOperationMonoHopUnidirectionalPayment of msg: MonoHopUnidirectionalPaymentMsg * newCommitments: Commitments
271+
| WeAcceptedMonoHopUnidirectionalPayment of newCommitments: Commitments
272+
270273
| WeAcceptedOperationAddHTLC of msg: UpdateAddHTLCMsg * newCommitments: Commitments
271274
| WeAcceptedUpdateAddHTLC of newCommitments: Commitments
272275

@@ -358,6 +361,26 @@ type ChannelState =
358361
(fun v cc -> match cc with
359362
| Normal _ -> Normal v
360363
| _ -> cc )
364+
member this.ChannelId: Option<ChannelId> =
365+
match this with
366+
| WaitForInitInternal
367+
| WaitForOpenChannel _
368+
| WaitForAcceptChannel _
369+
| WaitForFundingCreated _ -> None
370+
| WaitForFundingSigned data -> Some data.ChannelId
371+
| WaitForFundingConfirmed data -> Some data.ChannelId
372+
| WaitForFundingLocked data -> Some data.ChannelId
373+
| Normal data -> Some data.ChannelId
374+
| Shutdown data -> Some data.ChannelId
375+
| Negotiating data -> Some data.ChannelId
376+
| Closing data -> Some data.ChannelId
377+
| Closed _
378+
| Offline _
379+
| Syncing _
380+
| ErrFundingLost _
381+
| ErrFundingTimeOut _
382+
| ErrInformationLeak _ -> None
383+
361384
member this.Phase =
362385
match this with
363386
| WaitForInitInternal
@@ -377,3 +400,24 @@ type ChannelState =
377400
| ErrFundingLost _
378401
| ErrFundingTimeOut _
379402
| ErrInformationLeak _ -> Abnormal
403+
404+
member this.Commitments: Option<Commitments> =
405+
match this with
406+
| WaitForInitInternal
407+
| WaitForOpenChannel _
408+
| WaitForAcceptChannel _
409+
| WaitForFundingCreated _
410+
| WaitForFundingSigned _ -> None
411+
| WaitForFundingConfirmed data -> Some (data :> IHasCommitments).Commitments
412+
| WaitForFundingLocked data -> Some (data :> IHasCommitments).Commitments
413+
| Normal data -> Some (data :> IHasCommitments).Commitments
414+
| Shutdown data -> Some (data :> IHasCommitments).Commitments
415+
| Negotiating data -> Some (data :> IHasCommitments).Commitments
416+
| Closing data -> Some (data :> IHasCommitments).Commitments
417+
| Closed _
418+
| Offline _
419+
| Syncing _
420+
| ErrFundingLost _
421+
| ErrFundingTimeOut _
422+
| ErrInformationLeak _ -> None
423+

src/DotNetLightning.Core/Channel/ChannelValidation.fs

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

185+
let checkOurMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: MonoHopUnidirectionalPaymentMsg) =
186+
Validation.ofResult(MonoHopUnidirectionalPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
187+
|> Result.mapError(fun errs -> InvalidMonoHopUnidirectionalPayment { NetworkMsg = payment; Errors = errs })
188+
189+
let checkTheirMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: MonoHopUnidirectionalPaymentMsg) =
190+
Validation.ofResult(MonoHopUnidirectionalPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
191+
|> Result.mapError(fun errs -> InvalidMonoHopUnidirectionalPayment { NetworkMsg = payment; Errors = errs })
185192

186193
let checkOperationAddHTLC (state: NormalData) (op: OperationAddHTLC) =
187194
Validation.ofResult(UpdateAddHTLCValidation.checkExpiryIsNotPast op.CurrentHeight op.Expiry)

src/DotNetLightning.Core/Channel/Commitments.fs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,39 @@ type Commitments = {
183183
match remoteSigned, localSigned with
184184
| Some _, Some htlcIn -> htlcIn.Add |> Some
185185
| _ -> None
186+
187+
member this.SpendableBalance(): LNMoney =
188+
let remoteCommit =
189+
match this.RemoteNextCommitInfo with
190+
| RemoteNextCommitInfo.Waiting info -> info.NextRemoteCommit
191+
| RemoteNextCommitInfo.Revoked _info -> this.RemoteCommit
192+
let reducedRes =
193+
remoteCommit.Spec.Reduce(
194+
this.RemoteChanges.ACKed,
195+
this.LocalChanges.Proposed
196+
)
197+
let reduced =
198+
match reducedRes with
199+
| Error err ->
200+
failwithf
201+
"reducing commit failed even though we have not proposed any changes\
202+
error: %A"
203+
err
204+
| Ok reduced -> reduced
205+
let fees =
206+
if this.LocalParams.IsFunder then
207+
Transactions.commitTxFee this.RemoteParams.DustLimitSatoshis reduced
208+
|> LNMoney.FromMoney
209+
else
210+
LNMoney.Zero
211+
let channelReserve =
212+
this.RemoteParams.ChannelReserveSatoshis
213+
|> LNMoney.FromMoney
214+
let totalBalance = reduced.ToRemote
215+
let untrimmedSpendableBalance = totalBalance - channelReserve - fees
216+
let dustLimit =
217+
this.RemoteParams.DustLimitSatoshis
218+
|> LNMoney.FromMoney
219+
let untrimmedMax = LNMoney.Min(untrimmedSpendableBalance, dustLimit)
220+
let spendableBalance = LNMoney.Max(untrimmedMax, untrimmedSpendableBalance)
221+
spendableBalance

src/DotNetLightning.Core/Crypto/ShaChain.fs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
namespace DotNetLightning.Crypto
22

3+
open System
4+
35
type Node = {
46
Value: byte[]
57
Height: int32
@@ -16,8 +18,9 @@ module ShaChain =
1618
let flip (_input: byte[]) (_index: uint64): byte[] =
1719
failwith "Not implemented: ShaChain::flip"
1820

19-
let addHash (_receiver: ShaChain) (_hash: byte[]) (_index: uint64) =
20-
failwith "Not implemented: ShaChain::addHash"
21+
let addHash (receiver: ShaChain) (_hash: byte[]) (_index: uint64) =
22+
Console.WriteLine("WARNING: Not implemented: ShaChain::addHash")
23+
receiver
2124

2225
let getHash (_receiver: ShaChain)(_index: uint64) =
2326
failwith "Not implemented: ShaChain::getHash"

src/DotNetLightning.Core/DotNetLightning.Core.fsproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<When Condition="'$(BouncyCastle)'=='true'">
1515
<PropertyGroup>
1616
<OtherFlags>$(OtherFlags) -d:BouncyCastle</OtherFlags>
17-
<PackageId>DotNetLightning</PackageId>
17+
<PackageId>DotNetLightning.Kiss</PackageId>
1818
</PropertyGroup>
1919
</When>
2020
<Otherwise>

src/DotNetLightning.Core/Serialize/Msgs/Msgs.fs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ module internal TypeFlag =
101101
let ReplyChannelRange = 264us
102102
[<Literal>]
103103
let GossipTimestampFilter = 265us
104+
[<Literal>]
105+
let MonoHopUnidirectionalPayment = 42198us
104106

105107
type ILightningMsg = interface end
106108
type ISetupMsg = inherit ILightningMsg
@@ -192,6 +194,8 @@ module ILightningSerializable =
192194
deserialize<ReplyChannelRangeMsg>(ls) :> ILightningMsg
193195
| TypeFlag.GossipTimestampFilter ->
194196
deserialize<GossipTimestampFilterMsg>(ls) :> ILightningMsg
197+
| TypeFlag.MonoHopUnidirectionalPayment ->
198+
deserialize<MonoHopUnidirectionalPaymentMsg>(ls) :> ILightningMsg
195199
| x ->
196200
raise <| FormatException(sprintf "Unknown message type %d" x)
197201
let serializeWithFlags (ls: LightningWriterStream) (data: ILightningMsg) =
@@ -280,6 +284,9 @@ module ILightningSerializable =
280284
| :? GossipTimestampFilterMsg as d ->
281285
ls.Write(TypeFlag.GossipTimestampFilter, false)
282286
(d :> ILightningSerializable<GossipTimestampFilterMsg>).Serialize(ls)
287+
| :? MonoHopUnidirectionalPaymentMsg as d ->
288+
ls.Write(TypeFlag.MonoHopUnidirectionalPayment, false)
289+
(d :> ILightningSerializable<MonoHopUnidirectionalPaymentMsg>).Serialize(ls)
283290
| x -> failwithf "%A is not known lightning message. This should never happen" x
284291

285292
module LightningMsg =
@@ -1569,3 +1576,19 @@ type GossipTimestampFilterMsg = {
15691576
ls.Write(this.FirstTimestamp, false)
15701577
ls.Write(this.TimestampRange, false)
15711578

1579+
[<CLIMutable>]
1580+
type MonoHopUnidirectionalPaymentMsg = {
1581+
mutable ChannelId: ChannelId
1582+
mutable Amount: LNMoney
1583+
}
1584+
with
1585+
interface IHTLCMsg
1586+
interface IUpdateMsg
1587+
interface ILightningSerializable<MonoHopUnidirectionalPaymentMsg> with
1588+
member this.Deserialize(ls) =
1589+
this.ChannelId <- ls.ReadUInt256(true) |> ChannelId
1590+
this.Amount <- ls.ReadUInt64(false) |> LNMoney.MilliSatoshis
1591+
member this.Serialize(ls) =
1592+
ls.Write(this.ChannelId.Value.ToBytes())
1593+
ls.Write(this.Amount.MilliSatoshi, false)
1594+

0 commit comments

Comments
 (0)