Skip to content

Commit

Permalink
Merge pull request #4 from bitbankinc/test_ltc
Browse files Browse the repository at this point in the history
Test ltc
  • Loading branch information
joemphilips authored Jan 22, 2022
2 parents e2aa6e5 + ccfddb5 commit 14a4c11
Show file tree
Hide file tree
Showing 43 changed files with 548 additions and 274 deletions.
16 changes: 8 additions & 8 deletions LICENSE.Dependencies.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
## Dependent Softwares and its licenses.


| Name of the software | Its license | Usage in NLoop |
| :---: | :---: | :---: |
| [EventStoreDB][esdb] | [3-Clause BSD License][esdb-license] | To store everything including Swap state, secret keys, etc. |
| [NBitcon][nbitcoin] | [MIT License][nbitcoin-license] | To use Bitcoin-related types |
| [DotNetLightning][dnl] | MIT License | To use LN-related primitive types |
| [FsToolKit.ErrorHandling][fstoolkit] | [MIT license][fstoolkit-license] | For handling `Result`, `TaskResult` effectively |
| [FSharp.Contro.AsyncSeq][asyncseq] | [Apache License 2.0](asyncseq-license) | To use asynchronous sequences |
| [FSharp.SystemTextJson][fsharp-stj] | [MIT license][fsharp-stj-license] | For Json (de)serialization of F# Specific types such as Records and Unions
| Name of the software | Its license | Copyright | Usage in NLoop |
| :---: | :---: | :---: | :---: |
| [EventStoreDB][esdb] | [3-Clause BSD License][esdb-license] | | To store everything including Swap state, secret keys, etc. |
| [NBitcon][nbitcoin] | [MIT License][nbitcoin-license] | | To use Bitcoin-related types |
| [DotNetLightning][dnl] | MIT License | | To use LN-related primitive types |
| [FsToolKit.ErrorHandling][fstoolkit] | | [MIT license][fstoolkit-license] | For handling `Result`, `TaskResult` effectively |
| [FSharp.Contro.AsyncSeq][asyncseq] | | [Apache License 2.0](asyncseq-license) | To use asynchronous sequences |
| [FSharp.SystemTextJson][fsharp-stj] | | [MIT license][fsharp-stj-license] | For Json (de)serialization of F# Specific types such as Records and Unions


[esdb]:https://github.com/EventStore/EventStore
Expand Down
9 changes: 7 additions & 2 deletions LndClient/LndGrpcClient.fs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ type NLoopLndGrpcClient(settings: LndGrpcSettings, network: Network) =
req.PaymentRequest <- param.Invoice.ToString()
req.OutgoingChanIds.AddRange(param.OutgoingChannelIds |> Seq.map(fun c -> c.ToUInt64()))
req.FeeLimitSat <- param.MaxFee.Satoshi
req.TimeoutSeconds <- param.Invoice.Expiry.Second |> int
req.TimeoutSeconds <-
param.Invoice.Expiry.Second |> int
routerClient.SendPaymentV2(req).ResponseStream

let f (s:Payment) =
Expand Down Expand Up @@ -397,12 +398,16 @@ type NLoopLndGrpcClient(settings: LndGrpcSettings, network: Network) =
}
|> Error
}
member this.QueryRoutes(nodeId, amount, ct) =
member this.QueryRoutes(nodeId, amount, maybeOutgoingChanId, ct) =
task {
let ct = defaultArg ct CancellationToken.None
let req = QueryRoutesRequest()
req.PubKey <- nodeId.ToHex()
req.Amt <- amount.Satoshi
maybeOutgoingChanId
|> Option.iter(fun chanId ->
req.OutgoingChanId <- chanId.ToUInt64()
)
let! resp = client.QueryRoutesAsync(req, this.DefaultHeaders, this.Deadline, ct)
let r = resp.Routes.[0]
return
Expand Down
2 changes: 1 addition & 1 deletion LndClient/types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ type INLoopLightningClient =
-> Task<PaymentRequest>
abstract member Offer: req: SendPaymentRequest * ?ct: CancellationToken -> Task<Result<PaymentResult, string>>
abstract member GetInfo: ?ct: CancellationToken -> Task<obj>
abstract member QueryRoutes: nodeId: PubKey * amount: LNMoney * ?ct: CancellationToken -> Task<Route>
abstract member QueryRoutes: nodeId: PubKey * amount: LNMoney * ?maybeOutgoingChanId: ShortChannelId * ?ct: CancellationToken -> Task<Route>
abstract member OpenChannel: request: LndOpenChannelRequest * ?ct: CancellationToken -> Task<Result<OutPoint, LndOpenChannelError>>
abstract member ConnectPeer: nodeId: PubKey * host: string * ?ct: CancellationToken -> Task
abstract member ListChannels: ?ct: CancellationToken -> Task<ListChannelResponse list>
Expand Down
6 changes: 5 additions & 1 deletion NLoop.Domain/Swap.fs
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,6 @@ module Swap =
return []
}
return events @ additionalEvents

| NewBlock ({ Height = height; Block = block }, cc), In(oldHeight, loopIn) when loopIn.PairId.Quote = cc ->
let! events = (height, oldHeight) ||> checkHeight (block.Header.GetHash())
let! e =
Expand Down Expand Up @@ -833,6 +832,11 @@ module Swap =
return []
}
return events @ (e |> enhance)
| NewBlock _, Out _
| NewBlock _, In _ ->
// ignore if it is the cryptocode that we are not interested in.
assert false
return []
| UnConfirmBlock(blockHash), Out(_heightBefore, _)
| UnConfirmBlock(blockHash), In (_heightBefore, _) ->
return [BlockUnConfirmed { BlockHash = blockHash }] |> enhance
Expand Down
3 changes: 3 additions & 0 deletions NLoop.Domain/ValueObjects.fs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ module SupportedCryptoCode =
type PairId =
PairId of (struct (SupportedCryptoCode * SupportedCryptoCode))
with
member this.Reverse =
let struct (b, q) = this.Value
PairId(q, b)
member this.Value =
match this with
| PairId(a, b) -> struct(a, b)
Expand Down
10 changes: 5 additions & 5 deletions NLoop.Server/Actors/SwapExecutor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ type SwapExecutor(
getSwapKey: GetSwapKey,
getSwapPreimage: GetSwapPreimage,
getNetwork: GetNetwork,
getAddress: GetAddress,
swapActor: ISwapActor
)=

Expand Down Expand Up @@ -196,18 +197,17 @@ type SwapExecutor(
SwapDTO.LoopOutRequest.ClaimPublicKey = claimKey.PubKey
SwapDTO.LoopOutRequest.PreimageHash = preimageHash.Value }
swapServerClient.LoopOut req
let lnClient = lightningClientProvider.GetClient(pairId.Quote)

ct.ThrowIfCancellationRequested()
let! addr =
match req.Address with
| Some a -> Task.FromResult a
| Some a -> Task.FromResult (Ok a)
| None ->
lnClient.GetDepositAddress()
getAddress.Invoke(pairId.Base)
ct.ThrowIfCancellationRequested()
let loopOut = {
LoopOut.Id = outResponse.Id |> SwapId
LoopOut.ClaimKey = claimKey
ClaimKey = claimKey
OutgoingChanIds = req.OutgoingChannelIds
Preimage = preimage
RedeemScript = outResponse.RedeemScript
Expand Down Expand Up @@ -342,7 +342,7 @@ type SwapExecutor(
maybeSwapId <- swapId |> Some
let! exchangeRate =
tryGetExchangeRate(pairId, ct)
|> Task.map(Result.requireSome $"exchange rate for {pairId} is not available")
|> Task.map(Result.requireSome $"exchange rate for {PairId.toString(&pairId)} is not available")
match inResponse.Validate(invoice.PaymentHash.Value,
refundKey.PubKey,
loopIn.Amount,
Expand Down
20 changes: 7 additions & 13 deletions NLoop.Server/BlockChainListeners/BlockChainListener.fs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module private BlockchainListenerHelpers =
return failwith "unreachable!"
}
loop getBlock [] oldTip newTip
type BlockchainListener(opts: IOptions<NLoopOptions>,
type BlockchainListener(
loggerFactory: ILoggerFactory,
getBlockchainClient: GetBlockchainClient,
cc: SupportedCryptoCode,
Expand All @@ -76,7 +76,7 @@ type BlockchainListener(opts: IOptions<NLoopOptions>,

let _currentTipLockObj = obj()

let swaps = ConcurrentDictionary<SwapId, _>()
let swaps = ConcurrentDictionary<SwapId, unit>()

let newChainTipAsync newB = task {
logger.LogDebug $"New blockchain tip {newB} for {cc}"
Expand Down Expand Up @@ -158,14 +158,8 @@ type BlockchainListener(opts: IOptions<NLoopOptions>,
logger.LogError($"Error while handling block {ex}")
}

interface ISwapEventListener with
member this.RegisterSwap(id: SwapId) =
if not <| swaps.TryAdd(id, ()) then
logger.LogError($"Failed to add swap id {id}")

member this.RemoveSwap(swapId) =
if swaps.TryRemove(swapId) |> fst then
()
else
logger.LogError($"Failed to stop listening to {swapId}. This should never happen")

member this.RegisterSwap(id: SwapId) =
if not <| swaps.TryAdd(id, ()) then
logger.LogError($"Failed to add swap id {id}")
member this.RemoveSwap(swapId: SwapId) =
swaps.TryRemove(swapId) |> ignore
17 changes: 10 additions & 7 deletions NLoop.Server/BlockChainListeners/BlockchainListeners.fs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
namespace NLoop.Server

open System
open System.Collections.Concurrent
open System.Collections.Generic
open System.Threading.Tasks
open FSharp.Control.Tasks
open Microsoft.Extensions.Hosting
Expand Down Expand Up @@ -49,7 +47,7 @@ type BlockchainListeners(opts: IOptions<NLoopOptions>,
let cOpts = opts.Value.ChainOptions.[cc]
let startRPCListener cc = task {
let rpcListener =
RPCLongPollingBlockchainListener(opts, loggerFactory, getBlockchainClient, (fun () -> this.GetRewindLimit(cc)), getNetwork, swapActor, cc)
RPCLongPollingBlockchainListener(loggerFactory, getBlockchainClient, (fun () -> this.GetRewindLimit(cc)), getNetwork, swapActor, cc)
do! (rpcListener :> IHostedService).StartAsync(ct)
match listeners.TryAdd(cc, rpcListener :> BlockchainListener) with
| true -> ()
Expand All @@ -60,7 +58,7 @@ type BlockchainListeners(opts: IOptions<NLoopOptions>,
| None ->
do! startRPCListener cc
| Some addr ->
let zmqListener = ZmqBlockchainListener(opts, addr, loggerFactory, getBlockchainClient, getNetwork, swapActor, cc, (fun () -> this.GetRewindLimit(cc)))
let zmqListener = ZmqBlockchainListener(addr, loggerFactory, getBlockchainClient, getNetwork, swapActor, cc, (fun () -> this.GetRewindLimit(cc)))
match! zmqListener.CheckConnection ct with
| true ->
do! (zmqListener :> IHostedService).StartAsync(ct)
Expand Down Expand Up @@ -88,7 +86,12 @@ type BlockchainListeners(opts: IOptions<NLoopOptions>,
member this.CurrentHeight cc = this.CurrentHeight cc

interface ISwapEventListener with
member this.RegisterSwap(swapId) =
listeners |> Seq.iter(fun l -> (l.Value :> ISwapEventListener).RegisterSwap(swapId))
member this.RegisterSwap(swapId, group) =
match listeners.TryGetValue group.OnChainAsset with
| false, _ ->
()
| true, listener ->
listener.RegisterSwap(swapId)
member this.RemoveSwap(swapId) =
listeners |> Seq.iter(fun l -> (l.Value :> ISwapEventListener).RemoveSwap(swapId))
for l in listeners.Values do
l.RemoveSwap(swapId)
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ open Microsoft.Extensions.Logging
open NLoop.Domain
open NLoop.Server

type RPCLongPollingBlockchainListener(opts: IOptions<NLoopOptions>,
type RPCLongPollingBlockchainListener(
loggerFactory,
getBlockchainClient,
getRewindLimit,
getNetwork,
actor,
cc) =
inherit BlockchainListener(opts, loggerFactory, getBlockchainClient, cc, getNetwork, actor)
let logger = loggerFactory.CreateLogger<RPCLongPollingBlockchainListener>()
inherit BlockchainListener(loggerFactory, getBlockchainClient, cc, getNetwork, actor)
let _logger = loggerFactory.CreateLogger<RPCLongPollingBlockchainListener>()
let mutable _executingTask = null
let mutable _stoppingCts = null

Expand Down
8 changes: 4 additions & 4 deletions NLoop.Server/BlockChainListeners/ZmqBlockchainListener.fs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type ZmqClient(logger: ILogger<ZmqClient>, address) =
member private this.WorkerThread (onBlock: OnBlock, ct: CancellationToken) () =
while not <| ct.IsCancellationRequested do
let m = sock.ReceiveMultipartMessage(3)
let topic, body, sequence = (m.Item 0), (m.Item 1), (m.Item 2)
let topic, body, _sequence = (m.Item 0), (m.Item 1), (m.Item 2)
if topic.Buffer = rawblockB then
let b = Block.Parse(body.Buffer |> hex.EncodeData, Network.RegTest)
onBlock b
Expand All @@ -80,15 +80,15 @@ type ZmqClient(logger: ILogger<ZmqClient>, address) =
| ex ->
logger.LogError $"Failed to dispose {nameof(ZmqClient)}. this should never happen: {ex}"

type ZmqBlockchainListener(opts: IOptions<NLoopOptions>,
type ZmqBlockchainListener(
zmqAddress,
loggerFactory,
getBlockchainClient,
getNetwork,
actor,
cryptoCode,
rewindLimit: unit -> StartHeight) as this =
inherit BlockchainListener(opts, loggerFactory, getBlockchainClient, cryptoCode, getNetwork, actor)
rewindLimit: unit -> StartHeight) =
inherit BlockchainListener(loggerFactory, getBlockchainClient, cryptoCode, getNetwork, actor)
let zmqClient = new ZmqClient(loggerFactory.CreateLogger<_>(), zmqAddress)
let logger = loggerFactory.CreateLogger<ZmqBlockchainListener>()

Expand Down
14 changes: 14 additions & 0 deletions NLoop.Server/CommandLine.fs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,20 @@ module NLoopServerCommandLine =
a
o
// --- ---

let o = Option<string[]>($"--{nameof(NLoopOptions.Instance.Exchanges).ToLowerInvariant()}",
"The name of exchanges you want to use, nloop will use its public api to get the current\n" +
"exchange rate in case of multi-asset swap. (e.g. for validating fee after adjusting the price)\n" +
"The list of possible exchanges are those which supported by NuGetPackage named ExchangeSharp: " +
"https://github.com/jjxtra/ExchangeSharp \n" +
"you can specify this option multiple times. "+
"In that case, the median value of all exchanges will be used.\n" +
$"default is {NLoopOptions.Instance.Exchanges}")
o.Argument <-
let a = Argument<string[]>()
a.Arity <- ArgumentArity.ZeroOrMore
a
o
]


Expand Down
57 changes: 31 additions & 26 deletions NLoop.Server/GiraffeExtensions.fs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
namespace NLoop.Server

open System.Threading.Tasks
open FsToolkit.ErrorHandling
open System.Text.Json
open DotNetLightning.Utils
open Giraffe
open LndClient
open Microsoft.AspNetCore.Http
open FSharp.Control.Tasks.Affine
open Microsoft.Extensions.Options
open Microsoft.Extensions.Logging
open NBitcoin
open NLoop.Domain
Expand Down Expand Up @@ -63,37 +61,44 @@ module CustomHandlers =
return! error503 errorMsg next ctx
}

open System.Threading.Tasks
let internal checkWeHaveRouteToCounterParty(offChainCryptoCode: SupportedCryptoCode) (amt: Money) (chanIdsSpecified: ShortChannelId[]) =
fun (next: HttpFunc) ( ctx: HttpContext) -> task {
let cli = ctx.GetService<ILightningClientProvider>().GetClient(offChainCryptoCode)
let boltzCli = ctx.GetService<ISwapServerClient>()
let nodesT = boltzCli.GetNodes()
let! nodes = nodesT
let mutable maybeResult = None
let! nodes = boltzCli.GetNodes()
let logger =
ctx
.GetLogger<_>()
for kv in nodes.Nodes do
if maybeResult.IsSome then () else
try
let! r = cli.QueryRoutes(kv.Value.NodeKey, amt.ToLNMoney())
match nodes.Nodes |> Seq.tryFind(fun kv -> kv.Key = offChainCryptoCode.ToString()) with
| None ->
return! errorBadRequest [$"counterparty server does not support {offChainCryptoCode.ToString()} as an off-chain currency"] next ctx
| Some kv ->
if chanIdsSpecified.Length = 0 then
let! r = cli.QueryRoutes(kv.Value.NodeKey, amt.ToLNMoney())
if (r.Value.Length > 0) then
maybeResult <- Some r
with
| ex ->
logger
.LogError $"{ex}"

if maybeResult.IsNone then
return! error503 $"Failed to find route to Boltz server. Make sure the channel is open and active" next ctx
else
let chanIds =
maybeResult.Value.Value
|> List.head
|> fun firstRoute -> firstRoute.ShortChannelId
logger.LogDebug($"paying through the channel {chanIds} ({chanIds.ToUInt64()})")
return! next ctx
let chanIds = r.Value.Head.ShortChannelId
logger.LogDebug($"paying through the channel {chanIds} ({chanIds.ToUInt64()})")
return! next ctx
else
return! error503 $"Failed to find route to Boltz server. Make sure you have open and active channel" next ctx
else
let! routes =
chanIdsSpecified
|> Array.map(fun chanId ->
cli.QueryRoutes(kv.Value.NodeKey, amt.ToLNMoney(), chanId)
)
|> Task.WhenAll
let foundRoutes = routes |> Array.filter(fun r -> r.Value.Length > 0)
if foundRoutes.Length > 0 then
let chanIds =
foundRoutes
|> Array.map(fun r -> r.Value.Head.ShortChannelId)
|> Array.toList
logger.LogDebug($"paying through the channel {chanIds} ({chanIds |> List.map(fun cId -> cId.ToUInt64())})")
return! next ctx
else
let msg = $"Failed to find route to Boltz server. Make sure the channels you specified is open and active"
return! error503 msg next ctx
}

let internal validateFeeLimitAgainstServerQuote(req: LoopOutRequest) =
Expand Down
2 changes: 1 addition & 1 deletion NLoop.Server/Interfaces.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ open NLoop.Domain.Utils
open NLoop.Server.DTOs

type ISwapEventListener =
abstract member RegisterSwap: swapId: SwapId -> unit
abstract member RegisterSwap: swapId: SwapId * group: Swap.Group -> unit
abstract member RemoveSwap: swapId: SwapId -> unit

type ILightningClientProvider =
Expand Down
4 changes: 2 additions & 2 deletions NLoop.Server/LightningClientProvider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ open NLoop.Domain

type LightningClientProvider(logger: ILogger<LightningClientProvider>,
opts: IOptions<NLoopOptions>,
getNetwork: GetNetwork,
httpClientFactory: IHttpClientFactory) =
getNetwork: GetNetwork
) =
let clients = Dictionary<SupportedCryptoCode, INLoopLightningClient>()
let settings = opts.Value.GetLndGrpcSettings()
do
Expand Down
1 change: 1 addition & 0 deletions NLoop.Server/NLoop.Server.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
<Version>0.0.8.1-alpha</Version>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\BoltzClient\BoltzClient.fsproj" />
Expand Down
Loading

0 comments on commit 14a4c11

Please sign in to comment.