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); } }
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); }
public override LNMoney?Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return(LNMoney.MilliSatoshis(reader.GetInt64())); }
public void CanCallSendPayment() { using 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, _pool)); 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, _pool, secret)); Assert.Contains("Cannot route when there are no outbound routes away from us", e.Message); }