Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Geewallet payments #1

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 46 additions & 16 deletions src/DotNetLightning.Core/Channel/Channel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ module Channel =
RemoteNextHTLCId = HTLCId.Zero
OriginChannels = Map.empty
// we will receive their next per-commitment point in the next msg, so we temporarily put a random byte array
RemoteNextCommitInfo = DataEncoders.HexEncoder() .DecodeData("0101010101010101010101010101010101010101010101010101010101010101") |> Key |> fun k -> k.PubKey |> Choice2Of2
RemoteNextCommitInfo = DataEncoders.HexEncoder() .DecodeData("0101010101010101010101010101010101010101010101010101010101010101") |> Key |> fun k -> k.PubKey |> RemoteNextCommitInfo.Revoked
RemotePerCommitmentSecrets = ShaChain.Zero
ChannelId =
msg.ChannelId }
Expand Down Expand Up @@ -316,7 +316,7 @@ module Channel =
LocalNextHTLCId = HTLCId.Zero
RemoteNextHTLCId = HTLCId.Zero
OriginChannels = Map.empty
RemoteNextCommitInfo = DataEncoders.HexEncoder() .DecodeData("0101010101010101010101010101010101010101010101010101010101010101") |> Key |> fun k -> k.PubKey |> Choice2Of2
RemoteNextCommitInfo = DataEncoders.HexEncoder() .DecodeData("0101010101010101010101010101010101010101010101010101010101010101") |> Key |> fun k -> k.PubKey |> RemoteNextCommitInfo.Revoked
RemotePerCommitmentSecrets = ShaChain.Zero
ChannelId = channelId }
let nextState = { WaitForFundingConfirmedData.Commitments = commitments
Expand Down Expand Up @@ -376,7 +376,7 @@ module Channel =
true,
None)
let nextState = { NormalData.Buried = true
Commitments = { state.Commitments with RemoteNextCommitInfo = Choice2Of2(msg.NextPerCommitmentPoint) }
Commitments = { state.Commitments with RemoteNextCommitInfo = RemoteNextCommitInfo.Revoked(msg.NextPerCommitmentPoint) }
ShortChannelId = state.ShortChannelId
ChannelAnnouncement = None
ChannelUpdate = initialChannelUpdate
Expand All @@ -388,6 +388,32 @@ module Channel =
[] |> Ok

// ---------- normal operation ---------
| ChannelState.Normal state, GeewalletPayment cmd when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please rename all occurrences of GeewalletPayment to MonoHopUnidirectionalPayment; I'd prefer DNL to not mention geewallet at all

sprintf "Could not send geewallet payment %A since shutdown is already in progress." cmd
|> apiMisuse
| ChannelState.Normal state, GeewalletPayment cmd ->
result {
let payment: GeewalletPayment = {
ChannelId = state.Commitments.ChannelId
Amount = cmd.Amount
}
let commitments1 = state.Commitments.AddLocalProposal(payment)

let remoteCommit1 =
match commitments1.RemoteNextCommitInfo with
| RemoteNextCommitInfo.WaitingForRevocation info -> info.NextRemoteCommit
| RemoteNextCommitInfo.Revoked _info -> commitments1.RemoteCommit
let! reduced = remoteCommit1.Spec.Reduce(commitments1.RemoteChanges.ACKed, commitments1.LocalChanges.Proposed) |> expectTransactionError
do! Validation.checkOurGeewalletPaymentIsAcceptableWithCurrentSpec reduced commitments1 payment
return [ WeAcceptedCMDGeewalletPayment(payment, commitments1) ]
}
| ChannelState.Normal state, ApplyGeewalletPayment msg ->
result {
let commitments1 = state.Commitments.AddRemoteProposal(msg)
let! reduced = commitments1.LocalCommit.Spec.Reduce (commitments1.LocalChanges.ACKed, commitments1.RemoteChanges.Proposed) |> expectTransactionError
do! Validation.checkTheirGeewalletPaymentIsAcceptableWithCurrentSpec reduced commitments1 msg
return [ WeAcceptedGeewalletPayment commitments1 ]
}
| ChannelState.Normal state, AddHTLC cmd when state.LocalShutdown.IsSome || state.RemoteShutdown.IsSome ->
sprintf "Could not add new HTLC %A since shutdown is already in progress." cmd
|> apiMisuse
Expand All @@ -409,8 +435,8 @@ module Channel =
// we need to base the next current commitment on the last sig we sent, even if we didn't yet receive their revocation
let remoteCommit1 =
match commitments1.RemoteNextCommitInfo with
| Choice1Of2 info -> info.NextRemoteCommit
| Choice2Of2 _info -> commitments1.RemoteCommit
| RemoteNextCommitInfo.WaitingForRevocation info -> info.NextRemoteCommit
| RemoteNextCommitInfo.Revoked _info -> commitments1.RemoteCommit
let! reduced = remoteCommit1.Spec.Reduce(commitments1.RemoteChanges.ACKed, commitments1.LocalChanges.Proposed) |> expectTransactionError
do! Validation.checkOurUpdateAddHTLCIsAcceptableWithCurrentSpec reduced commitments1 add
return [ WeAcceptedCMDAddHTLC(add, commitments1) ]
Expand Down Expand Up @@ -459,9 +485,9 @@ module Channel =
| _ when (cm.LocalHasChanges() |> not) ->
// Ignore SignCommitment Command (nothing to sign)
return []
| Choice2Of2 _ ->
| RemoteNextCommitInfo.Revoked _ ->
return! cm |> Commitments.sendCommit (cs.Secp256k1Context) (cs.KeysRepository) (cs.Network)
| Choice1Of2 _ ->
| RemoteNextCommitInfo.WaitingForRevocation _ ->
// Already in the process of signing
return []
}
Expand All @@ -472,17 +498,17 @@ module Channel =
| ChannelState.Normal state, ApplyRevokeAndACK msg ->
let cm = state.Commitments
match cm.RemoteNextCommitInfo with
| Choice1Of2 _ when (msg.PerCommitmentSecret.ToPubKey() <> cm.RemoteCommit.RemotePerCommitmentPoint) ->
| RemoteNextCommitInfo.WaitingForRevocation _ when (msg.PerCommitmentSecret.ToPubKey() <> cm.RemoteCommit.RemotePerCommitmentPoint) ->
let errorMsg = sprintf "Invalid revoke_and_ack %A; must be %A" msg.PerCommitmentSecret cm.RemoteCommit.RemotePerCommitmentPoint
invalidRevokeAndACK msg errorMsg
| Choice2Of2 _ ->
| RemoteNextCommitInfo.Revoked _ ->
let errorMsg = sprintf "Unexpected revocation"
invalidRevokeAndACK msg errorMsg
| Choice1Of2({ NextRemoteCommit = theirNextCommit }) ->
| RemoteNextCommitInfo.WaitingForRevocation({ NextRemoteCommit = theirNextCommit }) ->
let commitments1 = { cm with LocalChanges = { cm.LocalChanges with Signed = []; ACKed = cm.LocalChanges.ACKed @ cm.LocalChanges.Signed }
RemoteChanges = { cm.RemoteChanges with Signed = [] }
RemoteCommit = theirNextCommit
RemoteNextCommitInfo = Choice2Of2(msg.NextPerCommitmentPoint)
RemoteNextCommitInfo = RemoteNextCommitInfo.Revoked(msg.NextPerCommitmentPoint)
RemotePerCommitmentSecrets = cm.RemotePerCommitmentSecrets.AddHash (msg.PerCommitmentSecret.ToByteArray(), 0xffffffffffffUL - cm.RemoteCommit.Index) }
let result = [ WeAcceptedRevokeAndACK(commitments1) ]
result |> Ok
Expand Down Expand Up @@ -528,12 +554,12 @@ module Channel =
// Are we in the middle of a signature?
match cm.RemoteNextCommitInfo with
// yes.
| Choice1Of2 waitingForRevocation ->
| RemoteNextCommitInfo.WaitingForRevocation waitingForRevocation ->
let nextCommitments = { state.Commitments with
RemoteNextCommitInfo = Choice1Of2({ waitingForRevocation with ReSignASAP = true }) }
RemoteNextCommitInfo = RemoteNextCommitInfo.WaitingForRevocation({ waitingForRevocation with ReSignASAP = true }) }
return [ AcceptedShutdownWhileWeHaveUnsignedOutgoingHTLCs(msg, nextCommitments) ]
// No. let's sign right away.
| Choice2Of2 _ ->
| RemoteNextCommitInfo.Revoked _ ->
return [ ChannelStateRequestedSignCommitment; AcceptedShutdownWhileWeHaveUnsignedOutgoingHTLCs(msg, cm) ]
else
let (localShutdown, sendList) = match state.LocalShutdown with
Expand Down Expand Up @@ -600,9 +626,9 @@ module Channel =
| _ when (not <| cm.LocalHasChanges()) ->
// nothing to sign
[] |> Ok
| Choice2Of2 _ ->
| RemoteNextCommitInfo.Revoked _ ->
cm |> Commitments.sendCommit (cs.Secp256k1Context) (cs.KeysRepository) (cs.Network)
| Choice1Of2 _waitForRevocation ->
| RemoteNextCommitInfo.WaitingForRevocation _waitForRevocation ->
// Already in the process of signing.
[] |> Ok
| Shutdown state, ApplyCommitmentSigned msg ->
Expand Down Expand Up @@ -730,8 +756,12 @@ module Channel =
{ c with State = ChannelState.Normal data }

// ----- normal operation --------
| WeAcceptedCMDGeewalletPayment(_, newCommitments), ChannelState.Normal d ->
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
| WeAcceptedCMDAddHTLC(_, newCommitments), ChannelState.Normal d ->
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
| WeAcceptedGeewalletPayment(newCommitments), ChannelState.Normal d ->
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }
| WeAcceptedUpdateAddHTLC(newCommitments), ChannelState.Normal d ->
{ c with State = ChannelState.Normal({ d with Commitments = newCommitments }) }

Expand Down
6 changes: 6 additions & 0 deletions src/DotNetLightning.Core/Channel/ChannelCommands.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ open NBitcoin
// Y88b d88P Y88b. .d88P 888 " 888 888 " 888 d8888888888 888 Y8888 888 .d88P Y88b d88P
// "Y8888P" "Y88888P" 888 888 888 888 d88P 888 888 Y888 8888888P" "Y8888P"

type CMDGeewalletPayment = {
Amount: LNMoney
}

type CMDAddHTLC = {
AmountMSat: LNMoney
PaymentHash: PaymentHash
Expand Down Expand Up @@ -206,6 +210,8 @@ type ChannelCommand =
| CreateChannelReestablish

// normal
| GeewalletPayment of CMDGeewalletPayment
| ApplyGeewalletPayment of msg: GeewalletPayment
| AddHTLC of CMDAddHTLC
| ApplyUpdateAddHTLC of msg: UpdateAddHTLC * currentHeight: BlockHeight
| FulfillHTLC of CMDFulfillHTLC
Expand Down
41 changes: 40 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
| InvalidGeewalletPayment of InvalidGeewalletPaymentError
| 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
| InvalidGeewalletPayment _ -> Close
| InvalidUpdateAddHTLC _ -> Close
| InvalidRevokeAndACK _ -> Close
| InvalidUpdateFee _ -> Close
Expand Down Expand Up @@ -145,6 +147,15 @@ and InvalidAcceptChannelError = {
Errors = e
}

and InvalidGeewalletPaymentError = {
Msg: GeewalletPayment
Errors: string list
}
with
static member Create msg e = {
Msg = msg
Errors = e
}
and InvalidUpdateAddHTLCError = {
Msg: UpdateAddHTLC
Errors: string list
Expand Down Expand Up @@ -458,6 +469,18 @@ module internal AcceptChannelMsgValidation =

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

module UpdateGeewalletPaymentWithContext =
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 geewallet 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 @@ -472,7 +495,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 GeewalletPaymentValidationWithContext =
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 geewallet 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: UpdateAddHTLC) =
let htlcValueInFlight = currentSpec.HTLCs |> Map.toSeq |> Seq.sumBy (fun (_, v) -> v.Add.AmountMSat)
Expand Down
3 changes: 3 additions & 0 deletions src/DotNetLightning.Core/Channel/ChannelTypes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ type ChannelEvent =
| BothFundingLocked of nextState: Data.NormalData

// -------- normal operation ------
| WeAcceptedCMDGeewalletPayment of msg: GeewalletPayment * newCommitments: Commitments
| WeAcceptedGeewalletPayment of newCommitments: Commitments

| WeAcceptedCMDAddHTLC of msg: UpdateAddHTLC * newCommitments: Commitments
| WeAcceptedUpdateAddHTLC of newCommitments: Commitments

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

let checkOurGeewalletPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: GeewalletPayment) =
Validation.ofResult(GeewalletPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
|> Result.mapError(InvalidGeewalletPaymentError.Create payment >> InvalidGeewalletPayment)

let checkTheirGeewalletPaymentIsAcceptableWithCurrentSpec (currentSpec) (state: Commitments) (payment: GeewalletPayment) =
Validation.ofResult(GeewalletPaymentValidationWithContext.checkWeHaveSufficientFunds state currentSpec)
|> Result.mapError(InvalidGeewalletPaymentError.Create payment >> InvalidGeewalletPayment)

let checkCMDAddHTLC (state: NormalData) (cmd: CMDAddHTLC) =
Validation.ofResult(UpdateAddHTLCValidation.checkExpiryIsNotPast cmd.CurrentHeight cmd.Expiry)
Expand Down
33 changes: 30 additions & 3 deletions src/DotNetLightning.Core/Channel/Commitments.fs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,30 @@ type WaitingForRevocation = {
(fun w -> w.ReSignASAP),
(fun v w -> { w with ReSignASAP = v })

type RemoteNextCommitInfo =
| WaitingForRevocation of WaitingForRevocation
| Revoked of PubKey
with
static member WaitingForRevocation_: Prism<RemoteNextCommitInfo, WaitingForRevocation> =
(fun remoteNextCommitInfo ->
match remoteNextCommitInfo with
| WaitingForRevocation waitingForRevocation -> Some waitingForRevocation
| Revoked _ -> None),
(fun waitingForRevocation remoteNextCommitInfo ->
match remoteNextCommitInfo with
| WaitingForRevocation _ -> WaitingForRevocation waitingForRevocation
| Revoked _ -> remoteNextCommitInfo)

static member Revoked_: Prism<RemoteNextCommitInfo, PubKey> =
(fun remoteNextCommitInfo ->
match remoteNextCommitInfo with
| WaitingForRevocation _ -> None
| Revoked pubKey -> Some pubKey),
(fun pubKey remoteNextCommitInfo ->
match remoteNextCommitInfo with
| WaitingForRevocation _ -> remoteNextCommitInfo
| Revoked pubKey -> Revoked pubKey)

type Commitments = {
LocalParams: LocalParams
RemoteParams: RemoteParams
Expand All @@ -100,7 +124,7 @@ type Commitments = {
LocalNextHTLCId: HTLCId
RemoteNextHTLCId: HTLCId
OriginChannels: Map<HTLCId, HTLCSource>
RemoteNextCommitInfo: Choice<WaitingForRevocation, PubKey>
RemoteNextCommitInfo: RemoteNextCommitInfo
RemotePerCommitmentSecrets: ShaChain
ChannelId: ChannelId
}
Expand Down Expand Up @@ -140,15 +164,18 @@ type Commitments = {
this.RemoteChanges.Proposed |> List.exists(fun p -> match p with | :? UpdateAddHTLC -> true | _ -> false)

member internal this.HasNoPendingHTLCs() =
this.LocalCommit.Spec.HTLCs.IsEmpty && this.RemoteCommit.Spec.HTLCs.IsEmpty && (this.RemoteNextCommitInfo |> function Choice1Of2 _ -> false | Choice2Of2 _ -> true)
this.LocalCommit.Spec.HTLCs.IsEmpty && this.RemoteCommit.Spec.HTLCs.IsEmpty && (this.RemoteNextCommitInfo |> function WaitingForRevocation _ -> false | Revoked _ -> true)

member internal this.GetHTLCCrossSigned(directionRelativeToLocal: Direction, htlcId: HTLCId): UpdateAddHTLC option =
let remoteSigned =
this.LocalCommit.Spec.HTLCs
|> Map.tryPick (fun k v -> if v.Direction = directionRelativeToLocal && v.Add.HTLCId = htlcId then Some v else None)

let localSigned =
let lens = Commitments.RemoteNextCommitInfo_ >-> Choice.choice1Of2_ >?> WaitingForRevocation.NextRemoteCommit_
let lens =
Commitments.RemoteNextCommitInfo_
>-> RemoteNextCommitInfo.WaitingForRevocation_
>?> WaitingForRevocation.NextRemoteCommit_
match Optic.get lens this with
| Some v -> v
| None -> this.RemoteCommit
Expand Down
Loading