Пример #1
0
        public static void ConfigureNRustLightningAuth(this IServiceCollection services, IConfiguration Configuration)
        {
            // configure lsat/macaroon authentication
            services.AddSingleton <IMacaroonSecretRepository, InMemoryMacaroonSecretRepository>();
            // services.AddSingleton<ILSATInvoiceProvider, InMemoryRepository>();

            string?ourServiceName = null;
            int    ourServiceTier = 0;
            var    lsatConfig     = Configuration.GetSection("LSAT");

            services.AddAuthentication(options =>
            {
                options.DefaultScheme          = LSATDefaults.Scheme;
                options.DefaultChallengeScheme = LSATDefaults.Scheme;
            }).AddLSATAuthentication(options =>
            {
                options.ServiceName = "nrustlightning";
                options.ServiceTier = ourServiceTier;
                lsatConfig.Bind(options);
                ourServiceName = options.ServiceName;
                ourServiceTier = options.ServiceTier;
                // we want to give users only read capability when they have payed for it. not write.
                options.MacaroonCaveats.Add($"{ourServiceName}{DotNetLightning.Payment.LSAT.Constants.CAPABILITIES_CONDITION_PREFIX}=read");
                int amount            = lsatConfig.GetOrDefault("amount", 1);
                options.InvoiceAmount = LNMoney.MilliSatoshis((long)amount);
            });
            services.AddAuthorization(options =>
            {
                options.AddPolicy("Readonly", policy =>
                {
                    policy.RequireClaim("service", $"{ourServiceName}:{ourServiceTier}");
                    policy.RequireClaim($"{ourServiceName}{DotNetLightning.Payment.LSAT.Constants.CAPABILITIES_CONDITION_PREFIX}", "read", "admin");
                });

                options.AddPolicy("Admin", policy =>
                {
                    policy.RequireClaim("service", $"{ourServiceName}:{ourServiceTier}");
                    policy.RequireClaim($"{ourServiceName}{DotNetLightning.Payment.LSAT.Constants.CAPABILITIES_CONDITION_PREFIX}", "admin");
                });
            });
        }
Пример #2
0
        public void CanCreateAndCloseChannel()
        {
            using var channelManager = PeerManagerTests.getTestPeerManager().ChannelManager;
            var pk = _pubKeys[0];

            channelManager.CreateChannel(pk, Money.Satoshis(100000), LNMoney.MilliSatoshis(1000L), UInt64.MaxValue);
            var c = channelManager.ListChannels(_pool);

            Assert.Single(c);
            Assert.Equal(c[0].RemoteNetworkId, pk);
            Assert.Equal(100000U, c[0].ChannelValueSatoshis);
            Assert.Equal(1000U, c[0].InboundCapacityMSat);
            Assert.Equal(100000UL * 1000UL - 1000UL, c[0].OutboundCapacityMSat);

            Assert.False(c[0].IsLive);
            channelManager.CloseChannel(c[0].ChannelId);
            var events = channelManager.GetAndClearPendingEvents(_pool);

            Assert.Empty(events);
            channelManager.Dispose();
        }
Пример #3
0
        public void CanCallSendPayment()
        {
            var peerMan     = getTestPeerManager();
            var paymentHash = Primitives.PaymentHash.NewPaymentHash(uint256.Parse("4141414141414141414141414141414141414141414141414141414141414142"));
            var lastHops    = new List <RouteHint>();
            var e           = Assert.Throws <FFIException>(() => peerMan.SendPayment(_keys[0].PubKey, paymentHash, lastHops, LNMoney.MilliSatoshis(100L), TEST_FINAL_CTLV));

            Assert.Contains("Cannot route when there are no outbound routes away from us", e.Message);

            var secret = uint256.Parse("4141414141414141414141414141414141414141414141414141414141414143");

            e = Assert.Throws <FFIException>(() => peerMan.SendPayment(_keys[0].PubKey, paymentHash, lastHops, LNMoney.MilliSatoshis(100L), TEST_FINAL_CTLV, secret));
            Assert.Contains("Cannot route when there are no outbound routes away from us", e.Message);
        }
Пример #4
0
        public async Task PayInvoice(PaymentRequest invoice, long?amountMSat = null, CancellationToken ct = default)
        {
            var amount = invoice.AmountValue?.Value.MilliSatoshi ?? amountMSat;

            if (amount is null)
            {
                throw new NRustLightningException($"You must specify payment amount if it is not included in invoice");
            }

            var n = _networkProvider.TryGetByInvoice(invoice);

            if (n is null)
            {
                throw new NRustLightningException($"Unknown invoice prefix {invoice.PrefixValue}");
            }
            var peerMan    = _peerManagerProvider.GetPeerManager(n);
            var failureTcs = new TaskCompletionSource <Event>();
            var successTcs = new TaskCompletionSource <Event>();

            _eventAggregator.Subscribe <Event>(e =>
            {
                if (e is Event.PaymentFailed paymentFailed && paymentFailed.Item.PaymentHash.Equals(invoice.PaymentHash))
                {
                    _logger.LogError($"Payment for invoice ({invoice}) failed");
                    failureTcs.SetResult(paymentFailed);
                }

                if (e is Event.PaymentSent paymentSent && paymentSent.Item.Hash.Equals(invoice.PaymentHash))
                {
                    _logger.LogInformation($"Payment for invoice ({invoice}) succeed");
                    successTcs.SetResult(paymentSent);
                }
            });
            peerMan.SendPayment(invoice.NodeIdValue.Item, invoice.PaymentHash, new List <RouteHint>(), LNMoney.MilliSatoshis(amount.Value), invoice.MinFinalCLTVExpiryDelta);
            peerMan.ProcessEvents();
            var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);

            cts.CancelAfter(TimeSpan.FromSeconds(_config.Value.PaymentTimeoutSec));
            var delay = Task.Delay(TimeSpan.FromMilliseconds(-1), cts.Token);
            var task  = Task.WhenAny(failureTcs.Task, successTcs.Task);
            await Task.WhenAny(task, delay);

            if (cts.IsCancellationRequested)
            {
                throw new NRustLightningException($"Payment for {invoice} did not finish in {_config.Value.PaymentTimeoutSec} seconds");
            }
            var resultEvent = await await task;

            if (resultEvent is Event.PaymentFailed paymentFailed)
            {
                if (paymentFailed.Item.RejectedByDest)
                {
                    throw new NRustLightningException($"Failed to pay! rejected by destination");
                }
                throw new NRustLightningException($"Failed to pay!");
            }

            if (resultEvent is Event.PaymentSent paymentSent)
            {
                await _invoiceRepository.SetPreimage(paymentSent.Item);
            }
        }
Пример #5
0
        public async Task PayInvoice(PaymentRequest invoice, long?amountMSat = null, CancellationToken ct = default)
        {
            var amount = invoice.AmountValue?.Value.MilliSatoshi ?? amountMSat;

            if (amount is null)
            {
                throw new NRustLightningException($"You must specify payment amount if it is not included in invoice");
            }

            var n = _networkProvider.TryGetByInvoice(invoice);

            if (n is null)
            {
                throw new NRustLightningException($"Unknown invoice prefix {invoice.PrefixValue}");
            }
            var peerMan    = _peerManagerProvider.GetPeerManager(n);
            var failureTcs = new TaskCompletionSource <Event>(TaskCreationOptions.RunContinuationsAsynchronously);
            var successTcs = new TaskCompletionSource <Event>(TaskCreationOptions.RunContinuationsAsynchronously);

            _eventAggregator.Subscribe <Event>(e =>
            {
                if (e is Event.PaymentFailed paymentF && paymentF.Item.PaymentHash.Equals(invoice.PaymentHash))
                {
                    _logger.LogError($"Payment for invoice ({invoice}) failed");
                    failureTcs.SetResult(paymentF);
                }

                if (e is Event.PaymentSent paymentS && paymentS.Item.Hash.Equals(invoice.PaymentHash))
                {
                    _logger.LogInformation($"Payment for invoice ({invoice}) succeed");
                    successTcs.SetResult(paymentS);
                }
            });
            peerMan.SendPayment(invoice.NodeIdValue.Item, invoice.PaymentHash, new List <RouteHint>(), LNMoney.MilliSatoshis(amount.Value), invoice.MinFinalCLTVExpiryDelta, _pool, invoice.PaymentSecret.Value);
            peerMan.ProcessEvents();

            // case 1: canceled by user.
            var cancelTcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);

            await using var _ = (ct.Register(state => { ((TaskCompletionSource <object>)state).TrySetResult(null); },
                                             cancelTcs));
            var cancelTask = cancelTcs.Task;

            // case 2: timeout.
            using var cts = new CancellationTokenSource();
            var timeoutTask = Task.Delay(TimeSpan.FromSeconds(_config.Value.PaymentTimeoutSec), cts.Token);

            // case 3: finishes
            var task = Task.WhenAny(failureTcs.Task, successTcs.Task);

            var resultTask = await Task.WhenAny(cancelTask, timeoutTask, task);

            if (resultTask == cancelTask)
            {
                throw new NRustLightningException($"Payment canceled.");
            }
            if (resultTask == timeoutTask)
            {
                throw new NRustLightningException($"Payment for {invoice} did not finish in {_config.Value.PaymentTimeoutSec} seconds");
            }
            cts.Cancel(); // cancel the timer task so it does not fire.

            var resultEvent = await await task;

            if (resultEvent is Event.PaymentFailed paymentFailed)
            {
                if (paymentFailed.Item.RejectedByDest)
                {
                    throw new NRustLightningException($"Failed to pay! rejected by destination");
                }
                throw new NRustLightningException($"Failed to pay!");
            }

            if (resultEvent is Event.PaymentSent paymentSent)
            {
                await _repository.SetPreimage(paymentSent.Item, ct);
            }
        }
Пример #6
0
 public override LNMoney?Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 {
     return(LNMoney.MilliSatoshis(reader.GetInt64()));
 }
Пример #7
0
        public async Task CanCallSendPayment()
        {
            var hostBuilder = new HostBuilder().ConfigureTestHost();

            using var host = await hostBuilder.StartAsync();

            var c = host.GetTestNRustLightningClient();
            var e = await Assert.ThrowsAsync <HttpRequestException>(async() => await c.PayToInvoiceAsync("lnbcrt1p03zqaapp54vmm7lg9fjjnm6v998v8cafzqcnzecjnqlq8dk55rhgzlrgstzasdqqcqzpgsp58vt9tjxuvx8jmy4q0wtdakdu24k6zlx8np67w8pxmvcmwcacsa3q9qy9qsqx8h8dmt0fsqel426tu0ayrscaty0pt4t3tg2rz0jpl7p3xyx6hynwhhc86h7apmmf9043n250275cuuad6npdasqclk0ga9htyq0v0qqjs26z3"));

            Assert.Equal("You must specify payment amount if it is not included in invoice", e.Message);
            e = await Assert.ThrowsAsync <HttpRequestException>(async() => await c.PayToInvoiceAsync("lnbcrt1p03zqaapp54vmm7lg9fjjnm6v998v8cafzqcnzecjnqlq8dk55rhgzlrgstzasdqqcqzpgsp58vt9tjxuvx8jmy4q0wtdakdu24k6zlx8np67w8pxmvcmwcacsa3q9qy9qsqx8h8dmt0fsqel426tu0ayrscaty0pt4t3tg2rz0jpl7p3xyx6hynwhhc86h7apmmf9043n250275cuuad6npdasqclk0ga9htyq0v0qqjs26z3", LNMoney.MilliSatoshis(100L)));

            Assert.Contains("Cannot route when there are no outbound routes away from us", e.Message);
        }