Skip to content

Commit 8195bd4

Browse files
canndrewknocte
authored andcommitted
Mono-hop unidirectional payments
1 parent 8cbdca3 commit 8195bd4

File tree

16 files changed

+332
-18
lines changed

16 files changed

+332
-18
lines changed

.github/workflows/publish_master.yml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,5 @@ jobs:
3232
run: |
3333
cd $GITHUB_WORKSPACE/src/DotNetLightning.Core
3434
dotnet pack . -p:Configuration=Release --version-suffix date`date +%Y%m%d-%H%M`-git-`echo $GITHUB_SHA | cut -c 1-7` -p:BouncyCastle=True
35-
dotnet nuget push ./bin/Release/DotNetLightning.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
35+
dotnet nuget push ./bin/Release/DotNetLightning.Kiss.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
3636
37-
- name: Upload nuget packages (native)
38-
run: |
39-
cd $GITHUB_WORKSPACE/src/DotNetLightning.Core
40-
dotnet pack . -p:Configuration=Release --version-suffix date`date +%Y%m%d-%H%M`-git-`echo $GITHUB_SHA | cut -c 1-7`-${{ matrix.RID }}
41-
dotnet nuget push ./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: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,32 @@ module Channel =
388388
[] |> Ok
389389

390390
// ---------- normal operation ---------
391+
| ChannelState.Normal state, MonoHopUnidirectionalPayment op when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
392+
sprintf "Could not send mono-hop unidirectional payment %A since shutdown is already in progress." op
393+
|> apiMisuse
394+
| ChannelState.Normal state, MonoHopUnidirectionalPayment op ->
395+
result {
396+
let payment: MonoHopUnidirectionalPayment = {
397+
ChannelId = state.Commitments.ChannelId
398+
Amount = op.Amount
399+
}
400+
let commitments1 = state.Commitments.AddLocalProposal(payment)
401+
402+
let remoteCommit1 =
403+
match commitments1.RemoteNextCommitInfo with
404+
| RemoteNextCommitInfo.Waiting 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 [ WeAcceptedOperationMonoHopUnidirectionalPayment(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 op when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
392418
sprintf "Could not add new HTLC %A since shutdown is already in progress." op
393419
|> apiMisuse
@@ -484,10 +510,9 @@ module Channel =
484510
RemoteCommit = theirNextCommit
485511
RemoteNextCommitInfo = RemoteNextCommitInfo.Revoked(msg.NextPerCommitmentPoint)
486512
RemotePerCommitmentSecrets = cm.RemotePerCommitmentSecrets.AddHash (msg.PerCommitmentSecret.ToByteArray(), 0xffffffffffffUL - cm.RemoteCommit.Index) }
513+
Console.WriteLine("WARNING: revocation is not implemented yet")
487514
let result = [ WeAcceptedRevokeAndACK(commitments1) ]
488515
result |> Ok
489-
failwith "needs update"
490-
491516

492517
| ChannelState.Normal state, ChannelCommand.Close cmd ->
493518
let localSPK = cmd.ScriptPubKey |> Option.defaultValue (state.Commitments.LocalParams.DefaultFinalScriptPubKey)
@@ -730,8 +755,12 @@ module Channel =
730755
{ c with State = ChannelState.Normal data }
731756

732757
// ----- normal operation --------
758+
| WeAcceptedOperationMonoHopUnidirectionalPayment(_, newCommitments), ChannelState.Normal normalData ->
759+
{ c with State = ChannelState.Normal({ normalData with Commitments = newCommitments }) }
733760
| WeAcceptedOperationAddHTLC(_, newCommitments), ChannelState.Normal d ->
734761
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
762+
| WeAcceptedMonoHopUnidirectionalPayment(newCommitments), ChannelState.Normal normalData ->
763+
{ c with State = ChannelState.Normal({ normalData with Commitments = newCommitments }) }
735764
| WeAcceptedUpdateAddHTLC(newCommitments), ChannelState.Normal d ->
736765
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
737766

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,11 @@ and InvalidAcceptChannelError = {
145147
Errors = e
146148
}
147149

150+
and InvalidMonoHopUnidirectionalPaymentError = {
151+
Msg: MonoHopUnidirectionalPayment
152+
Errors: string list
153+
}
154+
148155
and InvalidUpdateAddHTLCError = {
149156
Msg: UpdateAddHTLC
150157
Errors: string list
@@ -457,6 +464,22 @@ module internal AcceptChannelMsgValidation =
457464

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

467+
module UpdateMonoHopUnidirectionalPaymentWithContext =
468+
let internal checkWeHaveSufficientFunds (state: Commitments) (currentSpec) =
469+
let fees =
470+
if state.LocalParams.IsFunder then
471+
Transactions.commitTxFee state.RemoteParams.DustLimitSatoshis currentSpec
472+
else
473+
Money.Zero
474+
let missing = currentSpec.ToRemote.ToMoney() - state.RemoteParams.ChannelReserveSatoshis - fees
475+
if missing < Money.Zero then
476+
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"
477+
(currentSpec.ToRemote.ToMoney())
478+
state.RemoteParams.ChannelReserveSatoshis
479+
fees
480+
|> Error
481+
else
482+
Ok()
460483

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

474-
497+
module internal MonoHopUnidirectionalPaymentValidationWithContext =
498+
let checkWeHaveSufficientFunds (state: Commitments) (currentSpec) =
499+
let fees =
500+
if state.LocalParams.IsFunder then
501+
Transactions.commitTxFee state.RemoteParams.DustLimitSatoshis currentSpec
502+
else
503+
Money.Zero
504+
let missing = currentSpec.ToRemote.ToMoney() - state.RemoteParams.ChannelReserveSatoshis - fees
505+
if missing < Money.Zero then
506+
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"
507+
(currentSpec.ToRemote.ToMoney())
508+
state.RemoteParams.ChannelReserveSatoshis
509+
fees
510+
|> Error
511+
else
512+
Ok()
513+
475514
module internal UpdateAddHTLCValidationWithContext =
476515
let checkLessThanHTLCValueInFlightLimit (currentSpec: CommitmentSpec) (limit) (add: UpdateAddHTLC) =
477516
let htlcValueInFlight = currentSpec.HTLCs |> Map.toSeq |> Seq.sumBy (fun (_, v) -> v.Add.Amount)

src/DotNetLightning.Core/Channel/ChannelOperations.fs

Lines changed: 7 additions & 1 deletion
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: MonoHopUnidirectionalPayment
200206
| AddHTLC of OperationAddHTLC
201207
| ApplyUpdateAddHTLC of msg: UpdateAddHTLC * currentHeight: BlockHeight
202208
| FulfillHTLC of OperationFulfillHTLC
@@ -220,4 +226,4 @@ type ChannelCommand =
220226
// else
221227
| ForceClose
222228
| GetState
223-
| GetStateData
229+
| GetStateData

src/DotNetLightning.Core/Channel/ChannelTypes.fs

Lines changed: 44 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+
| WeAcceptedOperationMonoHopUnidirectionalPayment of msg: MonoHopUnidirectionalPayment * newCommitments: Commitments
268+
| WeAcceptedMonoHopUnidirectionalPayment of newCommitments: Commitments
269+
267270
| WeAcceptedOperationAddHTLC 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
@@ -374,3 +397,24 @@ type ChannelState =
374397
| ErrFundingLost _
375398
| ErrFundingTimeOut _
376399
| ErrInformationLeak _ -> Abnormal
400+
401+
member this.Commitments: Option<Commitments> =
402+
match this with
403+
| WaitForInitInternal
404+
| WaitForOpenChannel _
405+
| WaitForAcceptChannel _
406+
| WaitForFundingCreated _
407+
| WaitForFundingSigned _ -> None
408+
| WaitForFundingConfirmed data -> Some (data :> IHasCommitments).Commitments
409+
| WaitForFundingLocked data -> Some (data :> IHasCommitments).Commitments
410+
| Normal data -> Some (data :> IHasCommitments).Commitments
411+
| Shutdown data -> Some (data :> IHasCommitments).Commitments
412+
| Negotiating data -> Some (data :> IHasCommitments).Commitments
413+
| Closing data -> Some (data :> IHasCommitments).Commitments
414+
| Closed _
415+
| Offline _
416+
| Syncing _
417+
| ErrFundingLost _
418+
| ErrFundingTimeOut _
419+
| ErrInformationLeak _ -> None
420+

src/DotNetLightning.Core/Channel/ChannelValidation.fs

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

176+
let checkOurMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: MonoHopUnidirectionalPayment) =
177+
Validation.ofResult(MonoHopUnidirectionalPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
178+
|> Result.mapError(fun errs -> InvalidMonoHopUnidirectionalPayment { Msg = payment; Errors = errs })
179+
180+
let checkTheirMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: MonoHopUnidirectionalPayment) =
181+
Validation.ofResult(MonoHopUnidirectionalPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
182+
|> Result.mapError(fun errs -> InvalidMonoHopUnidirectionalPayment { Msg = payment; Errors = errs })
176183

177184
let checkOperationAddHTLC (state: NormalData) (op: OperationAddHTLC) =
178185
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
@@ -8,7 +8,7 @@
88
<When Condition="'$(BouncyCastle)'=='true'">
99
<PropertyGroup>
1010
<OtherFlags>$(OtherFlags) -d:BouncyCastle</OtherFlags>
11-
<PackageId>DotNetLightning</PackageId>
11+
<PackageId>DotNetLightning.Kiss</PackageId>
1212
</PropertyGroup>
1313
</When>
1414
<Otherwise>

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ module internal TypeFlag =
8383
let ReplyChannelRange = 264us
8484
[<Literal>]
8585
let GossipTimestampFilter = 265us
86+
[<Literal>]
87+
let MonoHopUnidirectionalPayment = 42198us
8688

8789
type ILightningMsg = interface end
8890
type ISetupMsg = inherit ILightningMsg
@@ -174,6 +176,8 @@ module ILightningSerializable =
174176
deserialize<ReplyChannelRange>(ls) :> ILightningMsg
175177
| TypeFlag.GossipTimestampFilter ->
176178
deserialize<GossipTimestampFilter>(ls) :> ILightningMsg
179+
| TypeFlag.MonoHopUnidirectionalPayment ->
180+
deserialize<MonoHopUnidirectionalPayment>(ls) :> ILightningMsg
177181
| x ->
178182
raise <| FormatException(sprintf "Unknown message type %d" x)
179183
let serializeWithFlags (ls: LightningWriterStream) (data: ILightningMsg) =
@@ -262,6 +266,9 @@ module ILightningSerializable =
262266
| :? GossipTimestampFilter as d ->
263267
ls.Write(TypeFlag.GossipTimestampFilter, false)
264268
(d :> ILightningSerializable<GossipTimestampFilter>).Serialize(ls)
269+
| :? MonoHopUnidirectionalPayment as d ->
270+
ls.Write(TypeFlag.MonoHopUnidirectionalPayment, false)
271+
(d :> ILightningSerializable<MonoHopUnidirectionalPayment>).Serialize(ls)
265272
| x -> failwithf "%A is not known lightning message. This should never happen" x
266273

267274
module LightningMsg =
@@ -1543,3 +1550,19 @@ type GossipTimestampFilter = {
15431550
ls.Write(this.FirstTimestamp, false)
15441551
ls.Write(this.TimestampRange, false)
15451552

1553+
[<CLIMutable>]
1554+
type MonoHopUnidirectionalPayment = {
1555+
mutable ChannelId: ChannelId
1556+
mutable Amount: LNMoney
1557+
}
1558+
with
1559+
interface IHTLCMsg
1560+
interface IUpdateMsg
1561+
interface ILightningSerializable<MonoHopUnidirectionalPayment> with
1562+
member this.Deserialize(ls) =
1563+
this.ChannelId <- ls.ReadUInt256(true) |> ChannelId
1564+
this.Amount <- ls.ReadUInt64(false) |> LNMoney.MilliSatoshis
1565+
member this.Serialize(ls) =
1566+
ls.Write(this.ChannelId.Value.ToBytes())
1567+
ls.Write(this.Amount.MilliSatoshi, false)
1568+

0 commit comments

Comments
 (0)