2020-06-28 21:44:35 -05:00
using System ;
2018-02-26 00:48:12 +09:00
using System.Linq ;
2020-06-28 17:55:27 +09:00
using System.Threading ;
2018-02-26 00:48:12 +09:00
using System.Threading.Tasks ;
2020-06-28 17:55:27 +09:00
using BTCPayServer.Data ;
using BTCPayServer.Lightning ;
2018-02-26 00:48:12 +09:00
using BTCPayServer.Models.StoreViewModels ;
using BTCPayServer.Payments ;
using BTCPayServer.Payments.Lightning ;
2020-06-28 17:55:27 +09:00
using Microsoft.AspNetCore.Mvc ;
using Microsoft.Extensions.DependencyInjection ;
2018-02-26 00:48:12 +09:00
namespace BTCPayServer.Controllers
{
public partial class StoresController
{
2021-03-31 13:23:36 +02:00
[HttpGet("{storeId}/lightning/{cryptoCode}")]
2021-04-19 16:21:50 +02:00
public IActionResult SetupLightningNode ( string storeId , string cryptoCode )
2018-02-26 00:48:12 +09:00
{
2018-04-30 02:33:42 +09:00
var store = HttpContext . GetStoreData ( ) ;
2018-02-26 00:48:12 +09:00
if ( store = = null )
return NotFound ( ) ;
2021-03-31 13:23:36 +02:00
var vm = new LightningNodeViewModel
2018-07-27 13:37:16 +02:00
{
CryptoCode = cryptoCode ,
2019-01-07 09:52:27 +01:00
StoreId = storeId
2018-07-27 13:37:16 +02:00
} ;
2018-03-21 03:05:51 +09:00
SetExistingValues ( store , vm ) ;
2018-02-26 00:48:12 +09:00
return View ( vm ) ;
}
2021-03-31 13:23:36 +02:00
[HttpPost("{storeId}/lightning/{cryptoCode}")]
2021-04-19 16:21:50 +02:00
public async Task < IActionResult > SetupLightningNode ( string storeId , LightningNodeViewModel vm , string command , string cryptoCode )
2018-02-26 00:48:12 +09:00
{
2018-03-21 02:48:11 +09:00
vm . CryptoCode = cryptoCode ;
2018-04-30 02:33:42 +09:00
var store = HttpContext . GetStoreData ( ) ;
2018-02-26 00:48:12 +09:00
if ( store = = null )
return NotFound ( ) ;
2018-03-21 02:09:25 +09:00
2021-03-31 13:23:36 +02:00
vm . CanUseInternalNode = CanUseInternalLightning ( ) ;
var network = vm . CryptoCode = = null ? null : _ExplorerProvider . GetNetwork ( vm . CryptoCode ) ;
2018-03-20 11:59:43 +09:00
if ( network = = null )
2018-02-26 00:48:12 +09:00
{
2018-03-21 02:48:11 +09:00
ModelState . AddModelError ( nameof ( vm . CryptoCode ) , "Invalid network" ) ;
2018-02-26 00:48:12 +09:00
return View ( vm ) ;
}
2021-03-31 13:23:36 +02:00
var paymentMethodId = new PaymentMethodId ( network . CryptoCode , PaymentTypes . LightningLike ) ;
2021-04-16 15:31:09 +02:00
var lightning = GetExistingLightningSupportedPaymentMethod ( vm . CryptoCode , store ) ;
2021-03-31 13:23:36 +02:00
LightningSupportedPaymentMethod paymentMethod = null ;
if ( vm . LightningNodeType = = LightningNodeType . Internal )
2021-03-02 11:11:58 +09:00
{
if ( ! CanUseInternalLightning ( ) )
{
2021-03-31 13:23:36 +02:00
ModelState . AddModelError ( nameof ( vm . ConnectionString ) , "You are not authorized to use the internal lightning node" ) ;
2021-03-02 11:11:58 +09:00
return View ( vm ) ;
}
2021-03-31 13:23:36 +02:00
paymentMethod = new LightningSupportedPaymentMethod
2021-03-02 11:11:58 +09:00
{
CryptoCode = paymentMethodId . CryptoCode
} ;
paymentMethod . SetInternalNode ( ) ;
}
2021-03-31 13:23:36 +02:00
else
2018-02-26 00:48:12 +09:00
{
2021-03-31 13:23:36 +02:00
if ( string . IsNullOrEmpty ( vm . ConnectionString ) )
{
ModelState . AddModelError ( nameof ( vm . ConnectionString ) , "Please provide a connection string" ) ;
return View ( vm ) ;
}
2018-07-01 15:45:08 +09:00
if ( ! LightningConnectionString . TryParse ( vm . ConnectionString , false , out var connectionString , out var error ) )
2018-02-26 00:48:12 +09:00
{
2018-07-01 15:45:08 +09:00
ModelState . AddModelError ( nameof ( vm . ConnectionString ) , $"Invalid URL ({error})" ) ;
2018-02-26 00:48:12 +09:00
return View ( vm ) ;
}
2020-06-28 17:55:27 +09:00
if ( connectionString . ConnectionType = = LightningConnectionType . LndGRPC )
2018-07-23 11:11:00 +09:00
{
ModelState . AddModelError ( nameof ( vm . ConnectionString ) , $"BTCPay does not support gRPC connections" ) ;
return View ( vm ) ;
}
2021-03-02 11:11:58 +09:00
if ( ! User . IsInRole ( Roles . ServerAdmin ) & & ! connectionString . IsSafe ( ) )
2018-07-10 12:46:04 +09:00
{
2021-03-31 13:23:36 +02:00
ModelState . AddModelError ( nameof ( vm . ConnectionString ) , "You are not a server admin, so the connection string should not contain 'cookiefilepath', 'macaroondirectorypath', 'macaroonfilepath', and should not point to a local ip or to a dns name ending with '.internal', '.local', '.lan' or '.'." ) ;
2018-02-26 18:58:02 +09:00
return View ( vm ) ;
}
2021-03-31 13:23:36 +02:00
paymentMethod = new LightningSupportedPaymentMethod
2018-02-26 00:48:12 +09:00
{
CryptoCode = paymentMethodId . CryptoCode
} ;
2018-03-21 00:31:19 +09:00
paymentMethod . SetLightningUrl ( connectionString ) ;
2018-02-26 00:48:12 +09:00
}
2018-06-23 22:03:51 -05:00
2018-07-27 13:37:16 +02:00
switch ( command )
2018-02-26 00:48:12 +09:00
{
2018-07-27 13:37:16 +02:00
case "save" :
2018-08-08 17:32:16 +09:00
var storeBlob = store . GetStoreBlob ( ) ;
2020-10-17 15:52:41 -05:00
storeBlob . Hints . Lightning = false ;
2018-08-08 17:32:16 +09:00
store . SetStoreBlob ( storeBlob ) ;
2019-05-09 16:11:09 +09:00
store . SetSupportedPaymentMethod ( paymentMethodId , paymentMethod ) ;
2018-07-27 13:37:16 +02:00
await _Repo . UpdateStore ( store ) ;
2021-04-20 09:09:32 +02:00
TempData [ WellKnownTempData . SuccessMessage ] = $"{network.CryptoCode} Lightning node updated." ;
2021-03-31 13:23:36 +02:00
return RedirectToAction ( nameof ( UpdateStore ) , new { storeId } ) ;
2018-07-27 13:37:16 +02:00
case "test" :
2019-05-29 09:43:50 +00:00
var handler = _ServiceProvider . GetRequiredService < LightningLikePaymentHandler > ( ) ;
2018-07-27 13:37:16 +02:00
try
2018-04-09 16:25:31 +09:00
{
2021-03-31 13:23:36 +02:00
var info = await handler . GetNodeInfo ( Request . IsOnion ( ) , paymentMethod , network ) ;
2018-07-27 13:37:16 +02:00
if ( ! vm . SkipPortTest )
2018-04-09 16:25:31 +09:00
{
2021-03-31 13:23:36 +02:00
using var cts = new CancellationTokenSource ( TimeSpan . FromSeconds ( 20 ) ) ;
await handler . TestConnection ( info , cts . Token ) ;
2018-04-09 16:25:31 +09:00
}
2021-04-20 09:09:32 +02:00
TempData [ WellKnownTempData . SuccessMessage ] = $"Connection to the Lightning node successful. Your node address: {info}" ;
2018-07-27 13:37:16 +02:00
}
catch ( Exception ex )
{
2019-10-31 12:29:59 +09:00
TempData [ WellKnownTempData . ErrorMessage ] = ex . Message ;
2018-07-27 13:37:16 +02:00
return View ( vm ) ;
2018-04-09 16:25:31 +09:00
}
2018-02-26 00:48:12 +09:00
return View ( vm ) ;
2021-03-31 13:23:36 +02:00
2018-07-27 13:37:16 +02:00
default :
return View ( vm ) ;
2018-02-26 00:48:12 +09:00
}
}
2021-03-31 13:23:36 +02:00
2021-04-16 15:31:09 +02:00
[HttpPost("{storeId}/lightning/{cryptoCode}/status")]
public async Task < IActionResult > SetLightningNodeEnabled ( string storeId , string cryptoCode , bool enabled )
{
var store = HttpContext . GetStoreData ( ) ;
if ( store = = null )
return NotFound ( ) ;
var network = cryptoCode = = null ? null : _ExplorerProvider . GetNetwork ( cryptoCode ) ;
if ( network = = null )
return NotFound ( ) ;
var lightning = GetExistingLightningSupportedPaymentMethod ( cryptoCode , store ) ;
if ( lightning = = null )
return NotFound ( ) ;
2021-04-20 09:10:23 +02:00
var paymentMethodId = new PaymentMethodId ( network . CryptoCode , PaymentTypes . LightningLike ) ;
var storeBlob = store . GetStoreBlob ( ) ;
storeBlob . SetExcluded ( paymentMethodId , ! enabled ) ;
store . SetStoreBlob ( storeBlob ) ;
await _Repo . UpdateStore ( store ) ;
TempData [ WellKnownTempData . SuccessMessage ] = $"{network.CryptoCode} Lightning payments are now {(enabled ? " enabled " : " disabled ")} for this store." ;
2021-04-16 15:31:09 +02:00
return RedirectToAction ( nameof ( UpdateStore ) , new { storeId } ) ;
}
2018-02-26 18:58:02 +09:00
private bool CanUseInternalLightning ( )
{
2021-03-02 11:11:58 +09:00
return User . IsInRole ( Roles . ServerAdmin ) | | _CssThemeManager . AllowLightningInternalNodeForAll ;
2018-02-26 18:58:02 +09:00
}
2021-03-31 13:23:36 +02:00
private void SetExistingValues ( StoreData store , LightningNodeViewModel vm )
{
2021-04-19 16:21:50 +02:00
vm . CanUseInternalNode = CanUseInternalLightning ( ) ;
2021-03-31 13:23:36 +02:00
var lightning = GetExistingLightningSupportedPaymentMethod ( vm . CryptoCode , store ) ;
if ( lightning ! = null )
{
vm . LightningNodeType = lightning . IsInternalNode ? LightningNodeType . Internal : LightningNodeType . Custom ;
vm . ConnectionString = lightning . GetDisplayableConnectionString ( ) ;
}
2021-04-19 16:21:50 +02:00
else
{
vm . LightningNodeType = vm . CanUseInternalNode ? LightningNodeType . Internal : LightningNodeType . Custom ;
}
2021-03-31 13:23:36 +02:00
}
private LightningSupportedPaymentMethod GetExistingLightningSupportedPaymentMethod ( string cryptoCode , StoreData store )
{
var id = new PaymentMethodId ( cryptoCode , PaymentTypes . LightningLike ) ;
var existing = store . GetSupportedPaymentMethods ( _NetworkProvider )
. OfType < LightningSupportedPaymentMethod > ( )
. FirstOrDefault ( d = > d . PaymentId = = id ) ;
return existing ;
}
2018-02-26 00:48:12 +09:00
}
}