Fix several potential NullReferenceException (Fix #4017)

This commit is contained in:
nicolas.dorier
2022-08-04 12:07:59 +09:00
parent c71e671311
commit 40e39b82e8
7 changed files with 73 additions and 53 deletions

View File

@@ -1,3 +1,4 @@
#nullable enable
using System; using System;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;

View File

@@ -1,3 +1,4 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.Common; using System.Data.Common;
@@ -71,9 +72,12 @@ public class StoreWalletBalance : ViewComponent
using CancellationTokenSource cts = new (TimeSpan.FromSeconds(3)); using CancellationTokenSource cts = new (TimeSpan.FromSeconds(3));
var wallet = _walletProvider.GetWallet(_networkProvider.DefaultNetwork); var wallet = _walletProvider.GetWallet(_networkProvider.DefaultNetwork);
var derivation = store.GetDerivationSchemeSettings(_networkProvider, walletId.CryptoCode); var derivation = store.GetDerivationSchemeSettings(_networkProvider, walletId.CryptoCode);
if (derivation is not null)
{
var balance = await wallet.GetBalance(derivation.AccountDerivation, cts.Token); var balance = await wallet.GetBalance(derivation.AccountDerivation, cts.Token);
vm.Balance = balance.Available.GetValue(); vm.Balance = balance.Available.GetValue();
} }
}
return View(vm); return View(vm);
} }

View File

@@ -1,3 +1,4 @@
#nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -40,14 +41,14 @@ namespace BTCPayServer.Components.WalletNav
var network = _networkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode); var network = _networkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
var wallet = _walletProvider.GetWallet(network); var wallet = _walletProvider.GetWallet(network);
var derivation = store.GetDerivationSchemeSettings(_networkProvider, walletId.CryptoCode); var derivation = store.GetDerivationSchemeSettings(_networkProvider, walletId.CryptoCode);
var balance = await _walletsController.GetBalanceString(wallet, derivation.AccountDerivation); var balance = await _walletsController.GetBalanceString(wallet, derivation?.AccountDerivation);
var vm = new WalletNavViewModel var vm = new WalletNavViewModel
{ {
WalletId = walletId, WalletId = walletId,
Network = network, Network = network,
Balance = balance, Balance = balance,
Label = derivation.Label ?? $"{store.StoreName} {walletId.CryptoCode} Wallet" Label = derivation?.Label ?? $"{store.StoreName} {walletId.CryptoCode} Wallet"
}; };
return View(vm); return View(vm);

View File

@@ -86,8 +86,7 @@ namespace BTCPayServer.Controllers
// we just assume that it is 20 blocks // we just assume that it is 20 blocks
var assumedFeeRate = await fr.GetFeeRateAsync(20); var assumedFeeRate = await fr.GetFeeRateAsync(20);
var settings = (this.GetCurrentStore().GetDerivationSchemeSettings(NetworkProvider, network.CryptoCode)); var derivationScheme = (this.GetCurrentStore().GetDerivationSchemeSettings(NetworkProvider, network.CryptoCode))?.AccountDerivation;
var derivationScheme = settings.AccountDerivation;
if (derivationScheme is null) if (derivationScheme is null)
return NotFound(); return NotFound();

View File

@@ -1,3 +1,4 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@@ -123,10 +124,10 @@ namespace BTCPayServer.Controllers
// does not work // does not work
[ModelBinder(typeof(WalletIdModelBinder))] [ModelBinder(typeof(WalletIdModelBinder))]
WalletId walletId, string transactionId, WalletId walletId, string transactionId,
string addlabel = null, string? addlabel = null,
string addlabelclick = null, string? addlabelclick = null,
string addcomment = null, string? addcomment = null,
string removelabel = null) string? removelabel = null)
{ {
addlabel = addlabel ?? addlabelclick; addlabel = addlabel ?? addlabelclick;
// Hack necessary when the user enter a empty comment and submit. // Hack necessary when the user enter a empty comment and submit.
@@ -141,7 +142,7 @@ namespace BTCPayServer.Controllers
catch { } catch { }
///////// /////////
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); var paymentMethod = GetDerivationSchemeSettings(walletId);
if (paymentMethod == null) if (paymentMethod == null)
return NotFound(); return NotFound();
@@ -159,7 +160,7 @@ namespace BTCPayServer.Controllers
var rawLabel = await _labelFactory.BuildLabel( var rawLabel = await _labelFactory.BuildLabel(
walletBlobInfo, walletBlobInfo,
Request, Request!,
walletTransactionInfo, walletTransactionInfo,
walletId, walletId,
transactionId, transactionId,
@@ -256,12 +257,12 @@ namespace BTCPayServer.Controllers
public async Task<IActionResult> WalletTransactions( public async Task<IActionResult> WalletTransactions(
[ModelBinder(typeof(WalletIdModelBinder))] [ModelBinder(typeof(WalletIdModelBinder))]
WalletId walletId, WalletId walletId,
string labelFilter = null, string? labelFilter = null,
int skip = 0, int skip = 0,
int count = 50 int count = 50
) )
{ {
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); var paymentMethod = GetDerivationSchemeSettings(walletId);
if (paymentMethod == null) if (paymentMethod == null)
return NotFound(); return NotFound();
@@ -340,14 +341,14 @@ namespace BTCPayServer.Controllers
[HttpGet("{walletId}/receive")] [HttpGet("{walletId}/receive")]
public IActionResult WalletReceive([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, public IActionResult WalletReceive([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId,
[FromQuery] string returnUrl = null) [FromQuery] string? returnUrl = null)
{ {
if (walletId?.StoreId == null) if (walletId?.StoreId == null)
return NotFound(); return NotFound();
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); var paymentMethod = GetDerivationSchemeSettings(walletId);
if (paymentMethod == null) if (paymentMethod == null)
return NotFound(); return NotFound();
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode); var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
if (network == null) if (network == null)
return NotFound(); return NotFound();
var store = GetCurrentStore(); var store = GetCurrentStore();
@@ -376,10 +377,10 @@ namespace BTCPayServer.Controllers
{ {
if (walletId?.StoreId == null) if (walletId?.StoreId == null)
return NotFound(); return NotFound();
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); var paymentMethod = GetDerivationSchemeSettings(walletId);
if (paymentMethod == null) if (paymentMethod == null)
return NotFound(); return NotFound();
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode); var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
if (network == null) if (network == null)
return NotFound(); return NotFound();
switch (command) switch (command)
@@ -424,7 +425,7 @@ namespace BTCPayServer.Controllers
await b.SendBatchAsync(); await b.SendBatchAsync();
await cheater.CashCow.GenerateAsync(1); await cheater.CashCow.GenerateAsync(1);
var factory = ServiceProvider.GetService<NBXplorerConnectionFactory>(); var factory = ServiceProvider.GetRequiredService<NBXplorerConnectionFactory>();
// Wait it sync... // Wait it sync...
await Task.Delay(1000); await Task.Delay(1000);
@@ -450,16 +451,16 @@ namespace BTCPayServer.Controllers
[HttpGet("{walletId}/send")] [HttpGet("{walletId}/send")]
public async Task<IActionResult> WalletSend( public async Task<IActionResult> WalletSend(
[ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId,
string defaultDestination = null, string defaultAmount = null, string[] bip21 = null, string? defaultDestination = null, string? defaultAmount = null, string[]? bip21 = null,
[FromQuery] string returnUrl = null) [FromQuery] string? returnUrl = null)
{ {
if (walletId?.StoreId == null) if (walletId?.StoreId == null)
return NotFound(); return NotFound();
var store = await Repository.FindStore(walletId.StoreId, GetUserId()); var store = await Repository.FindStore(walletId.StoreId, GetUserId());
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); var paymentMethod = GetDerivationSchemeSettings(walletId);
if (paymentMethod == null) if (paymentMethod == null || store is null)
return NotFound(); return NotFound();
var network = this.NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode); var network = this.NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
if (network == null || network.ReadonlyWallet) if (network == null || network.ReadonlyWallet)
return NotFound(); return NotFound();
var storeData = store.GetStoreBlob(); var storeData = store.GetStoreBlob();
@@ -560,7 +561,7 @@ namespace BTCPayServer.Controllers
return View(model); return View(model);
} }
private async Task<string> GetSeed(WalletId walletId, BTCPayNetwork network) private async Task<string?> GetSeed(WalletId walletId, BTCPayNetwork network)
{ {
return await CanUseHotWallet() && return await CanUseHotWallet() &&
GetDerivationSchemeSettings(walletId) is DerivationSchemeSettings s && GetDerivationSchemeSettings(walletId) is DerivationSchemeSettings s &&
@@ -584,7 +585,7 @@ namespace BTCPayServer.Controllers
var store = await Repository.FindStore(walletId.StoreId, GetUserId()); var store = await Repository.FindStore(walletId.StoreId, GetUserId());
if (store == null) if (store == null)
return NotFound(); return NotFound();
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode); var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
if (network == null || network.ReadonlyWallet) if (network == null || network.ReadonlyWallet)
return NotFound(); return NotFound();
@@ -604,6 +605,8 @@ namespace BTCPayServer.Controllers
if (vm.InputSelection) if (vm.InputSelection)
{ {
var schemeSettings = GetDerivationSchemeSettings(walletId); var schemeSettings = GetDerivationSchemeSettings(walletId);
if (schemeSettings is null)
return NotFound();
var walletBlobAsync = await WalletRepository.GetWalletInfo(walletId); var walletBlobAsync = await WalletRepository.GetWalletInfo(walletId);
var walletTransactionsInfoAsync = await WalletRepository.GetWalletTransactionsInfo(walletId); var walletTransactionsInfoAsync = await WalletRepository.GetWalletTransactionsInfo(walletId);
@@ -633,7 +636,7 @@ namespace BTCPayServer.Controllers
ModelState.Clear(); ModelState.Clear();
return View(vm); return View(vm);
} }
vm.Outputs ??= new();
if (!string.IsNullOrEmpty(bip21)) if (!string.IsNullOrEmpty(bip21))
{ {
if (!vm.Outputs.Any()) if (!vm.Outputs.Any())
@@ -751,7 +754,9 @@ namespace BTCPayServer.Controllers
if (!ModelState.IsValid) if (!ModelState.IsValid)
return View(vm); return View(vm);
DerivationSchemeSettings derivationScheme = GetDerivationSchemeSettings(walletId); var derivationScheme = GetDerivationSchemeSettings(walletId);
if (derivationScheme is null)
return NotFound();
CreatePSBTResponse psbtResponse; CreatePSBTResponse psbtResponse;
if (command == "schedule") if (command == "schedule")
{ {
@@ -761,14 +766,14 @@ namespace BTCPayServer.Controllers
{ {
Destination = new AddressClaimDestination( Destination = new AddressClaimDestination(
BitcoinAddress.Create(output.DestinationAddress, network.NBitcoinNetwork)), BitcoinAddress.Create(output.DestinationAddress, network.NBitcoinNetwork)),
Value = output.Amount.Value, Value = output.Amount,
PaymentMethodId = pmi, PaymentMethodId = pmi,
StoreId = walletId.StoreId, StoreId = walletId.StoreId,
PreApprove = true, PreApprove = true,
}).ToArray(); }).ToArray();
var someFailed = false; var someFailed = false;
string message = null; string? message = null;
string errorMessage = null; string? errorMessage = null;
var result = new Dictionary<ClaimRequest, ClaimRequest.ClaimResult>(); var result = new Dictionary<ClaimRequest, ClaimRequest.ClaimResult>();
foreach (ClaimRequest claimRequest in claims) foreach (ClaimRequest claimRequest in claims)
{ {
@@ -873,7 +878,7 @@ namespace BTCPayServer.Controllers
private void LoadFromBIP21(WalletSendModel vm, string bip21, BTCPayNetwork network) private void LoadFromBIP21(WalletSendModel vm, string bip21, BTCPayNetwork network)
{ {
vm.Outputs ??= new List<WalletSendModel.TransactionOutput>(); vm.Outputs ??= new ();
try try
{ {
var uriBuilder = new NBitcoin.Payment.BitcoinUrlBuilder(bip21, network.NBitcoinNetwork); var uriBuilder = new NBitcoin.Payment.BitcoinUrlBuilder(bip21, network.NBitcoinNetwork);
@@ -881,7 +886,7 @@ namespace BTCPayServer.Controllers
vm.Outputs.Add(new WalletSendModel.TransactionOutput() vm.Outputs.Add(new WalletSendModel.TransactionOutput()
{ {
Amount = uriBuilder.Amount?.ToDecimal(MoneyUnit.BTC), Amount = uriBuilder.Amount?.ToDecimal(MoneyUnit.BTC),
DestinationAddress = uriBuilder.Address.ToString(), DestinationAddress = uriBuilder.Address?.ToString(),
SubtractFeesFromOutput = false, SubtractFeesFromOutput = false,
PayoutId = uriBuilder.UnknownParameters.ContainsKey("payout") PayoutId = uriBuilder.UnknownParameters.ContainsKey("payout")
? uriBuilder.UnknownParameters["payout"] ? uriBuilder.UnknownParameters["payout"]
@@ -1038,7 +1043,7 @@ namespace BTCPayServer.Controllers
throw new FormatException("Invalid value for crypto code"); throw new FormatException("Invalid value for crypto code");
ExtKey extKey = viewModel.GetExtKey(network.NBitcoinNetwork); ExtKey extKey = viewModel.GetExtKey(network.NBitcoinNetwork);
if (extKey == null) if (extKey is null)
{ {
ModelState.AddModelError(nameof(viewModel.SeedOrKey), ModelState.AddModelError(nameof(viewModel.SeedOrKey),
"Seed or Key was not in a valid format. It is either the 12/24 words or starts with xprv"); "Seed or Key was not in a valid format. It is either the 12/24 words or starts with xprv");
@@ -1055,9 +1060,13 @@ namespace BTCPayServer.Controllers
{ {
return View("SignWithSeed", viewModel); return View("SignWithSeed", viewModel);
} }
// It will never throw, this make nullable check below happy
ArgumentNullException.ThrowIfNull(extKey);
ExtKey signingKey = null; ExtKey? signingKey = null;
var settings = GetDerivationSchemeSettings(walletId); var settings = GetDerivationSchemeSettings(walletId);
if (settings is null)
return NotFound();
var signingKeySettings = settings.GetSigningAccountKeySettings(); var signingKeySettings = settings.GetSigningAccountKeySettings();
if (signingKeySettings.RootFingerprint is null) if (signingKeySettings.RootFingerprint is null)
signingKeySettings.RootFingerprint = extKey.GetPublicKey().GetHDFingerPrint(); signingKeySettings.RootFingerprint = extKey.GetPublicKey().GetHDFingerPrint();
@@ -1102,7 +1111,8 @@ namespace BTCPayServer.Controllers
} }
} }
ModelState.Remove(nameof(viewModel.SigningContext.PSBT)); ModelState.Remove(nameof(viewModel.SigningContext.PSBT));
viewModel.SigningContext.PSBT = psbt.ToBase64(); viewModel.SigningContext ??= new();
viewModel.SigningContext.PSBT = psbt?.ToBase64();
return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel
{ {
SigningKey = signingKey.GetWif(network.NBitcoinNetwork).ToString(), SigningKey = signingKey.GetWif(network.NBitcoinNetwork).ToString(),
@@ -1125,7 +1135,7 @@ namespace BTCPayServer.Controllers
{ {
if (walletId?.StoreId == null) if (walletId?.StoreId == null)
return NotFound(); return NotFound();
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); var paymentMethod = GetDerivationSchemeSettings(walletId);
if (paymentMethod == null) if (paymentMethod == null)
return NotFound(); return NotFound();
@@ -1156,7 +1166,7 @@ namespace BTCPayServer.Controllers
if (scanProgress.Status == ScanUTXOStatus.Complete) if (scanProgress.Status == ScanUTXOStatus.Complete)
{ {
vm.LastSuccess = scanProgress.Progress; vm.LastSuccess = scanProgress.Progress;
vm.TimeOfScan = (scanProgress.Progress.CompletedAt.Value - scanProgress.Progress.StartedAt) vm.TimeOfScan = (scanProgress.Progress!.CompletedAt!.Value - scanProgress.Progress.StartedAt)
.PrettyPrint(); .PrettyPrint();
} }
} }
@@ -1172,7 +1182,7 @@ namespace BTCPayServer.Controllers
{ {
if (walletId?.StoreId == null) if (walletId?.StoreId == null)
return NotFound(); return NotFound();
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); var paymentMethod = GetDerivationSchemeSettings(walletId);
if (paymentMethod == null) if (paymentMethod == null)
return NotFound(); return NotFound();
var explorer = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode); var explorer = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode);
@@ -1189,7 +1199,7 @@ namespace BTCPayServer.Controllers
return RedirectToAction(); return RedirectToAction();
} }
internal DerivationSchemeSettings GetDerivationSchemeSettings(WalletId walletId) internal DerivationSchemeSettings? GetDerivationSchemeSettings(WalletId walletId)
{ {
return GetCurrentStore().GetDerivationSchemeSettings(NetworkProvider, walletId.CryptoCode); return GetCurrentStore().GetDerivationSchemeSettings(NetworkProvider, walletId.CryptoCode);
} }
@@ -1209,8 +1219,10 @@ namespace BTCPayServer.Controllers
} }
} }
internal async Task<string> GetBalanceString(BTCPayWallet wallet, DerivationStrategyBase derivationStrategy) internal async Task<string> GetBalanceString(BTCPayWallet wallet, DerivationStrategyBase? derivationStrategy)
{ {
if (derivationStrategy is null)
return "--";
try try
{ {
return (await GetBalanceAsMoney(wallet, derivationStrategy)).ShowMoney(wallet.Network); return (await GetBalanceAsMoney(wallet, derivationStrategy)).ShowMoney(wallet.Network);
@@ -1252,7 +1264,7 @@ namespace BTCPayServer.Controllers
i++; i++;
} }
var backUrl = Url.Action(nameof(WalletTransactions), new { walletId }); var backUrl = Url.Action(nameof(WalletTransactions), new { walletId })!;
parameters.Add("returnUrl", backUrl); parameters.Add("returnUrl", backUrl);
parameters.Add("backUrl", backUrl); parameters.Add("backUrl", backUrl);
return View("PostRedirect", return View("PostRedirect",
@@ -1307,9 +1319,9 @@ namespace BTCPayServer.Controllers
[HttpGet("{walletId}/export")] [HttpGet("{walletId}/export")]
public async Task<IActionResult> Export( public async Task<IActionResult> Export(
[ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId,
string format, string labelFilter = null) string format, string? labelFilter = null)
{ {
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId); var paymentMethod = GetDerivationSchemeSettings(walletId);
if (paymentMethod == null) if (paymentMethod == null)
return NotFound(); return NotFound();
@@ -1345,15 +1357,15 @@ namespace BTCPayServer.Controllers
public class WalletReceiveViewModel public class WalletReceiveViewModel
{ {
public string CryptoImage { get; set; } public string? CryptoImage { get; set; }
public string CryptoCode { get; set; } public string? CryptoCode { get; set; }
public string Address { get; set; } public string? Address { get; set; }
public string PaymentLink { get; set; } public string? PaymentLink { get; set; }
public string ReturnUrl { get; set; } public string? ReturnUrl { get; set; }
} }
public class SendToAddressResult public class SendToAddressResult
{ {
[JsonProperty("psbt")] public string PSBT { get; set; } [JsonProperty("psbt")] public string? PSBT { get; set; }
} }
} }

View File

@@ -392,7 +392,9 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
return; return;
var derivationSchemeSettings = payout.StoreData var derivationSchemeSettings = payout.StoreData
.GetDerivationSchemeSettings(_btcPayNetworkProvider, newTransaction.CryptoCode).AccountDerivation; .GetDerivationSchemeSettings(_btcPayNetworkProvider, newTransaction.CryptoCode)?.AccountDerivation;
if (derivationSchemeSettings is null)
return;
var storeWalletMatched = (await _explorerClientProvider.GetExplorerClient(newTransaction.CryptoCode) var storeWalletMatched = (await _explorerClientProvider.GetExplorerClient(newTransaction.CryptoCode)
.GetTransactionAsync(derivationSchemeSettings, .GetTransactionAsync(derivationSchemeSettings,

View File

@@ -1,3 +1,4 @@
#nullable enable
using System.Linq; using System.Linq;
using BTCPayServer.Data; using BTCPayServer.Data;
@@ -5,7 +6,7 @@ namespace BTCPayServer
{ {
public static class StoreExtensions public static class StoreExtensions
{ {
public static DerivationSchemeSettings GetDerivationSchemeSettings(this StoreData store, BTCPayNetworkProvider networkProvider, string cryptoCode) public static DerivationSchemeSettings? GetDerivationSchemeSettings(this StoreData store, BTCPayNetworkProvider networkProvider, string cryptoCode)
{ {
var paymentMethod = store var paymentMethod = store
.GetSupportedPaymentMethods(networkProvider) .GetSupportedPaymentMethods(networkProvider)