Skip to content

Commit

Permalink
Mono-hop unidirectional payments
Browse files Browse the repository at this point in the history
  • Loading branch information
canndrew authored and knocte committed Aug 18, 2020
1 parent 48f7e60 commit cae6b93
Show file tree
Hide file tree
Showing 16 changed files with 339 additions and 16 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/publish_master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,5 @@ jobs:
if: startsWith(matrix.os, 'ubuntu')
run: |
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
dotnet nuget push ./src/DotNetLightning.Core/bin/Release/DotNetLightning.1*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
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
- name: Upload nuget packages (native)
run: |
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 }}"
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"
34 changes: 32 additions & 2 deletions src/DotNetLightning.Core/Channel/Channel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,32 @@ module Channel =
[] |> Ok

// ---------- normal operation ---------
| ChannelState.Normal state, MonoHopUnidirectionalPayment op when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
sprintf "Could not send mono-hop unidirectional payment %A since shutdown is already in progress." op
|> apiMisuse
| ChannelState.Normal state, MonoHopUnidirectionalPayment op ->
result {
let payment: MonoHopUnidirectionalPaymentMsg = {
ChannelId = state.Commitments.ChannelId
Amount = op.Amount
}
let commitments1 = state.Commitments.AddLocalProposal(payment)

let remoteCommit1 =
match commitments1.RemoteNextCommitInfo with
| RemoteNextCommitInfo.Waiting info -> info.NextRemoteCommit
| RemoteNextCommitInfo.Revoked _info -> commitments1.RemoteCommit
let! reduced = remoteCommit1.Spec.Reduce(commitments1.RemoteChanges.ACKed, commitments1.LocalChanges.Proposed) |> expectTransactionError
do! Validation.checkOurMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec reduced commitments1 payment
return [ WeAcceptedOperationMonoHopUnidirectionalPayment(payment, commitments1) ]
}
| ChannelState.Normal state, ApplyMonoHopUnidirectionalPayment msg ->
result {
let commitments1 = state.Commitments.AddRemoteProposal(msg)
let! reduced = commitments1.LocalCommit.Spec.Reduce (commitments1.LocalChanges.ACKed, commitments1.RemoteChanges.Proposed) |> expectTransactionError
do! Validation.checkTheirMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec reduced commitments1 msg
return [ WeAcceptedMonoHopUnidirectionalPayment commitments1 ]
}
| ChannelState.Normal state, AddHTLC op when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
sprintf "Could not add new HTLC %A since shutdown is already in progress." op
|> apiMisuse
Expand Down Expand Up @@ -514,8 +540,8 @@ module Channel =
RemoteCommit = theirNextCommit
RemoteNextCommitInfo = RemoteNextCommitInfo.Revoked msg.NextPerCommitmentPoint
RemotePerCommitmentSecrets = remotePerCommitmentSecrets }
let result = Ok [ WeAcceptedRevokeAndACK(commitments1) ]
failwith "needs update"
Console.WriteLine("WARNING: revocation is not implemented yet")
Ok [ WeAcceptedRevokeAndACK(commitments1) ]

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

// ----- normal operation --------
| WeAcceptedOperationMonoHopUnidirectionalPayment(_, newCommitments), ChannelState.Normal normalData ->
{ c with State = ChannelState.Normal({ normalData with Commitments = newCommitments }) }
| WeAcceptedOperationAddHTLC(_, newCommitments), ChannelState.Normal d ->
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
| WeAcceptedMonoHopUnidirectionalPayment(newCommitments), ChannelState.Normal normalData ->
{ c with State = ChannelState.Normal({ normalData with Commitments = newCommitments }) }
| WeAcceptedUpdateAddHTLC(newCommitments), ChannelState.Normal d ->
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }

Expand Down
48 changes: 47 additions & 1 deletion src/DotNetLightning.Core/Channel/ChannelError.fs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type ChannelError =
// --- case they sent unacceptable msg ---
| InvalidOpenChannel of InvalidOpenChannelError
| InvalidAcceptChannel of InvalidAcceptChannelError
| InvalidMonoHopUnidirectionalPayment of InvalidMonoHopUnidirectionalPaymentError
| InvalidUpdateAddHTLC of InvalidUpdateAddHTLCError
| InvalidRevokeAndACK of InvalidRevokeAndACKError
| InvalidUpdateFee of InvalidUpdateFeeError
Expand Down Expand Up @@ -69,6 +70,7 @@ type ChannelError =
| TheyCannotAffordFee (_, _, _) -> Close
| InvalidOpenChannel _ -> DistrustPeer
| InvalidAcceptChannel _ -> DistrustPeer
| InvalidMonoHopUnidirectionalPayment _ -> Close
| InvalidUpdateAddHTLC _ -> Close
| InvalidRevokeAndACK _ -> Close
| InvalidUpdateFee _ -> Close
Expand Down Expand Up @@ -179,6 +181,18 @@ and InvalidAcceptChannelError = {
member this.Message =
String.concat "; " this.Errors

and InvalidMonoHopUnidirectionalPaymentError = {
NetworkMsg: MonoHopUnidirectionalPaymentMsg
Errors: string list
}
with
static member Create msg e = {
NetworkMsg = msg
Errors = e
}
member this.Message =
String.concat "; " this.Errors

and InvalidUpdateAddHTLCError = {
NetworkMsg: UpdateAddHTLCMsg
Errors: string list
Expand Down Expand Up @@ -507,6 +521,22 @@ module internal AcceptChannelMsgValidation =

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

module UpdateMonoHopUnidirectionalPaymentWithContext =
let internal checkWeHaveSufficientFunds (state: Commitments) (currentSpec) =
let fees =
if state.LocalParams.IsFunder then
Transactions.commitTxFee state.RemoteParams.DustLimitSatoshis currentSpec
else
Money.Zero
let missing = currentSpec.ToRemote.ToMoney() - state.RemoteParams.ChannelReserveSatoshis - fees
if missing < Money.Zero then
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"
(currentSpec.ToRemote.ToMoney())
state.RemoteParams.ChannelReserveSatoshis
fees
|> Error
else
Ok()

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


module internal MonoHopUnidirectionalPaymentValidationWithContext =
let checkWeHaveSufficientFunds (state: Commitments) (currentSpec) =
let fees =
if state.LocalParams.IsFunder then
Transactions.commitTxFee state.RemoteParams.DustLimitSatoshis currentSpec
else
Money.Zero
let missing = currentSpec.ToRemote.ToMoney() - state.RemoteParams.ChannelReserveSatoshis - fees
if missing < Money.Zero then
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"
(currentSpec.ToRemote.ToMoney())
state.RemoteParams.ChannelReserveSatoshis
fees
|> Error
else
Ok()

module internal UpdateAddHTLCValidationWithContext =
let checkLessThanHTLCValueInFlightLimit (currentSpec: CommitmentSpec) (limit) (add: UpdateAddHTLCMsg) =
let htlcValueInFlight = currentSpec.HTLCs |> Map.toSeq |> Seq.sumBy (fun (_, v) -> v.Add.Amount)
Expand Down
6 changes: 6 additions & 0 deletions src/DotNetLightning.Core/Channel/ChannelOperations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ open DotNetLightning.Serialize

open NBitcoin

type OperationMonoHopUnidirectionalPayment = {
Amount: LNMoney
}

type OperationAddHTLC = {
Amount: LNMoney
PaymentHash: PaymentHash
Expand Down Expand Up @@ -197,6 +201,8 @@ type ChannelCommand =
| CreateChannelReestablish

// normal
| MonoHopUnidirectionalPayment of OperationMonoHopUnidirectionalPayment
| ApplyMonoHopUnidirectionalPayment of msg: MonoHopUnidirectionalPaymentMsg
| AddHTLC of OperationAddHTLC
| ApplyUpdateAddHTLC of msg: UpdateAddHTLCMsg * currentHeight: BlockHeight
| FulfillHTLC of OperationFulfillHTLC
Expand Down
44 changes: 44 additions & 0 deletions src/DotNetLightning.Core/Channel/ChannelTypes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ type ChannelEvent =
| BothFundingLocked of nextState: Data.NormalData

// -------- normal operation ------
| WeAcceptedOperationMonoHopUnidirectionalPayment of msg: MonoHopUnidirectionalPaymentMsg * newCommitments: Commitments
| WeAcceptedMonoHopUnidirectionalPayment of newCommitments: Commitments

| WeAcceptedOperationAddHTLC of msg: UpdateAddHTLCMsg * newCommitments: Commitments
| WeAcceptedUpdateAddHTLC of newCommitments: Commitments

Expand Down Expand Up @@ -358,6 +361,26 @@ type ChannelState =
(fun v cc -> match cc with
| Normal _ -> Normal v
| _ -> cc )
member this.ChannelId: Option<ChannelId> =
match this with
| WaitForInitInternal
| WaitForOpenChannel _
| WaitForAcceptChannel _
| WaitForFundingCreated _ -> None
| WaitForFundingSigned data -> Some data.ChannelId
| WaitForFundingConfirmed data -> Some data.ChannelId
| WaitForFundingLocked data -> Some data.ChannelId
| Normal data -> Some data.ChannelId
| Shutdown data -> Some data.ChannelId
| Negotiating data -> Some data.ChannelId
| Closing data -> Some data.ChannelId
| Closed _
| Offline _
| Syncing _
| ErrFundingLost _
| ErrFundingTimeOut _
| ErrInformationLeak _ -> None

member this.Phase =
match this with
| WaitForInitInternal
Expand All @@ -377,3 +400,24 @@ type ChannelState =
| ErrFundingLost _
| ErrFundingTimeOut _
| ErrInformationLeak _ -> Abnormal

member this.Commitments: Option<Commitments> =
match this with
| WaitForInitInternal
| WaitForOpenChannel _
| WaitForAcceptChannel _
| WaitForFundingCreated _
| WaitForFundingSigned _ -> None
| WaitForFundingConfirmed data -> Some (data :> IHasCommitments).Commitments
| WaitForFundingLocked data -> Some (data :> IHasCommitments).Commitments
| Normal data -> Some (data :> IHasCommitments).Commitments
| Shutdown data -> Some (data :> IHasCommitments).Commitments
| Negotiating data -> Some (data :> IHasCommitments).Commitments
| Closing data -> Some (data :> IHasCommitments).Commitments
| Closed _
| Offline _
| Syncing _
| ErrFundingLost _
| ErrFundingTimeOut _
| ErrInformationLeak _ -> None

7 changes: 7 additions & 0 deletions src/DotNetLightning.Core/Channel/ChannelValidation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ module internal Validation =
*> AcceptChannelMsgValidation.checkConfigPermits conf.PeerChannelConfigLimits msg
|> Result.mapError(InvalidAcceptChannelError.Create msg >> InvalidAcceptChannel)

let checkOurMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: MonoHopUnidirectionalPaymentMsg) =
Validation.ofResult(MonoHopUnidirectionalPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
|> Result.mapError(fun errs -> InvalidMonoHopUnidirectionalPayment { NetworkMsg = payment; Errors = errs })

let checkTheirMonoHopUnidirectionalPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: MonoHopUnidirectionalPaymentMsg) =
Validation.ofResult(MonoHopUnidirectionalPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
|> Result.mapError(fun errs -> InvalidMonoHopUnidirectionalPayment { NetworkMsg = payment; Errors = errs })

let checkOperationAddHTLC (state: NormalData) (op: OperationAddHTLC) =
Validation.ofResult(UpdateAddHTLCValidation.checkExpiryIsNotPast op.CurrentHeight op.Expiry)
Expand Down
36 changes: 36 additions & 0 deletions src/DotNetLightning.Core/Channel/Commitments.fs
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,39 @@ type Commitments = {
match remoteSigned, localSigned with
| Some _, Some htlcIn -> htlcIn.Add |> Some
| _ -> None

member this.SpendableBalance(): LNMoney =
let remoteCommit =
match this.RemoteNextCommitInfo with
| RemoteNextCommitInfo.Waiting info -> info.NextRemoteCommit
| RemoteNextCommitInfo.Revoked _info -> this.RemoteCommit
let reducedRes =
remoteCommit.Spec.Reduce(
this.RemoteChanges.ACKed,
this.LocalChanges.Proposed
)
let reduced =
match reducedRes with
| Error err ->
failwithf
"reducing commit failed even though we have not proposed any changes\
error: %A"
err
| Ok reduced -> reduced
let fees =
if this.LocalParams.IsFunder then
Transactions.commitTxFee this.RemoteParams.DustLimitSatoshis reduced
|> LNMoney.FromMoney
else
LNMoney.Zero
let channelReserve =
this.RemoteParams.ChannelReserveSatoshis
|> LNMoney.FromMoney
let totalBalance = reduced.ToRemote
let untrimmedSpendableBalance = totalBalance - channelReserve - fees
let dustLimit =
this.RemoteParams.DustLimitSatoshis
|> LNMoney.FromMoney
let untrimmedMax = LNMoney.Min(untrimmedSpendableBalance, dustLimit)
let spendableBalance = LNMoney.Max(untrimmedMax, untrimmedSpendableBalance)
spendableBalance
7 changes: 5 additions & 2 deletions src/DotNetLightning.Core/Crypto/ShaChain.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace DotNetLightning.Crypto

open System

type Node = {
Value: byte[]
Height: int32
Expand All @@ -16,8 +18,9 @@ module ShaChain =
let flip (_input: byte[]) (_index: uint64): byte[] =
failwith "Not implemented: ShaChain::flip"

let addHash (_receiver: ShaChain) (_hash: byte[]) (_index: uint64) =
failwith "Not implemented: ShaChain::addHash"
let addHash (receiver: ShaChain) (_hash: byte[]) (_index: uint64) =
Console.WriteLine("WARNING: Not implemented: ShaChain::addHash")
receiver

let getHash (_receiver: ShaChain)(_index: uint64) =
failwith "Not implemented: ShaChain::getHash"
Expand Down
2 changes: 1 addition & 1 deletion src/DotNetLightning.Core/DotNetLightning.Core.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<When Condition="'$(BouncyCastle)'=='true'">
<PropertyGroup>
<OtherFlags>$(OtherFlags) -d:BouncyCastle</OtherFlags>
<PackageId>DotNetLightning</PackageId>
<PackageId>DotNetLightning.Kiss</PackageId>
</PropertyGroup>
</When>
<Otherwise>
Expand Down
23 changes: 23 additions & 0 deletions src/DotNetLightning.Core/Serialize/Msgs/Msgs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ module internal TypeFlag =
let ReplyChannelRange = 264us
[<Literal>]
let GossipTimestampFilter = 265us
[<Literal>]
let MonoHopUnidirectionalPayment = 42198us

type ILightningMsg = interface end
type ISetupMsg = inherit ILightningMsg
Expand Down Expand Up @@ -192,6 +194,8 @@ module ILightningSerializable =
deserialize<ReplyChannelRangeMsg>(ls) :> ILightningMsg
| TypeFlag.GossipTimestampFilter ->
deserialize<GossipTimestampFilterMsg>(ls) :> ILightningMsg
| TypeFlag.MonoHopUnidirectionalPayment ->
deserialize<MonoHopUnidirectionalPaymentMsg>(ls) :> ILightningMsg
| x ->
raise <| FormatException(sprintf "Unknown message type %d" x)
let serializeWithFlags (ls: LightningWriterStream) (data: ILightningMsg) =
Expand Down Expand Up @@ -280,6 +284,9 @@ module ILightningSerializable =
| :? GossipTimestampFilterMsg as d ->
ls.Write(TypeFlag.GossipTimestampFilter, false)
(d :> ILightningSerializable<GossipTimestampFilterMsg>).Serialize(ls)
| :? MonoHopUnidirectionalPaymentMsg as d ->
ls.Write(TypeFlag.MonoHopUnidirectionalPayment, false)
(d :> ILightningSerializable<MonoHopUnidirectionalPaymentMsg>).Serialize(ls)
| x -> failwithf "%A is not known lightning message. This should never happen" x

module LightningMsg =
Expand Down Expand Up @@ -1569,3 +1576,19 @@ type GossipTimestampFilterMsg = {
ls.Write(this.FirstTimestamp, false)
ls.Write(this.TimestampRange, false)

[<CLIMutable>]
type MonoHopUnidirectionalPaymentMsg = {
mutable ChannelId: ChannelId
mutable Amount: LNMoney
}
with
interface IHTLCMsg
interface IUpdateMsg
interface ILightningSerializable<MonoHopUnidirectionalPaymentMsg> with
member this.Deserialize(ls) =
this.ChannelId <- ls.ReadUInt256(true) |> ChannelId
this.Amount <- ls.ReadUInt64(false) |> LNMoney.MilliSatoshis
member this.Serialize(ls) =
ls.Write(this.ChannelId.Value.ToBytes())
ls.Write(this.Amount.MilliSatoshi, false)

Loading

0 comments on commit cae6b93

Please sign in to comment.