public async Task <IActionResult> WalletTransactions( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId) { var store = await _Repo.FindStore(walletId.StoreId, GetUserId()); DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store); if (paymentMethod == null) { return(NotFound()); } var wallet = _walletProvider.GetWallet(paymentMethod.Network); var transactions = await wallet.FetchTransactions(paymentMethod.DerivationStrategyBase); var model = new ListTransactionsViewModel(); foreach (var tx in transactions.UnconfirmedTransactions.Transactions.Concat(transactions.ConfirmedTransactions.Transactions)) { var vm = new ListTransactionsViewModel.TransactionViewModel(); model.Transactions.Add(vm); vm.Id = tx.TransactionId.ToString(); vm.Link = string.Format(CultureInfo.InvariantCulture, paymentMethod.Network.BlockExplorerLink, vm.Id); vm.Timestamp = tx.Timestamp; vm.Positive = tx.BalanceChange >= Money.Zero; vm.Balance = tx.BalanceChange.ToString(); } model.Transactions = model.Transactions.OrderByDescending(t => t.Timestamp).ToList(); return(View(model)); }
/// <summary> /// Checks the address for received transactions and returns highest number of confimations from all transactions. /// </summary> /// <param name="merkleRoot">The private key of the source hash</param> /// <returns>-1 = no Timestamps, 0 = unconfirmed tx, above 0 is the number of confimations</returns> public AddressTimestamp GetTimestamp(byte[] merkleRoot) { var result = new AddressTimestamp(); var key = new Key(DerivationStrategy.GetKey(merkleRoot)); var address = key.PubKey.GetAddress(Network); result.Address = address.Hash.ToBytes(); // Address without Network format var balance = Repository.GetReceivedAsync(address.ToString()).GetAwaiter().GetResult(); //.ToWif()); if (balance == null || balance.Operations == null) { return(result); } var operation = balance.Operations.FirstOrDefault(); if (operation == null) { return(result); } //var operation = operations.ToList()[0]; result.Time = operation.FirstSeen.ToUniversalTime().ToUnixTimeSeconds(); result.Confirmations = operation.Confirmations; return(result); }
public async Task <IActionResult> WalletRescan( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, RescanWalletModel vm) { if (walletId?.StoreId == null) { return(NotFound()); } var store = await Repository.FindStore(walletId.StoreId, GetUserId()); DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store); if (paymentMethod == null) { return(NotFound()); } var explorer = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode); try { await explorer.ScanUTXOSetAsync(paymentMethod.DerivationStrategyBase, vm.BatchSize, vm.GapLimit, vm.StartingIndex); } catch (NBXplorerException ex) when(ex.Error.Code == "scanutxoset-in-progress") { } return(RedirectToAction()); }
public async Task <NetworkCoins> GetCoins(DerivationStrategy strategy, KnownState state, CancellationToken cancellation = default(CancellationToken)) { var client = _Client.GetExplorerClient(strategy.Network); if (client == null) { return new NetworkCoins() { TimestampedCoins = new NetworkCoins.TimestampedCoin[0], State = null, Strategy = strategy } } ; var changes = await client.SyncAsync(strategy.DerivationStrategyBase, state?.ConfirmedHash, state?.UnconfirmedHash, true, cancellation).ConfigureAwait(false); return(new NetworkCoins() { TimestampedCoins = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).Select(c => new NetworkCoins.TimestampedCoin() { Coin = c.AsCoin(), DateTime = c.Timestamp }).ToArray(), State = new KnownState() { ConfirmedHash = changes.Confirmed.Hash, UnconfirmedHash = changes.Unconfirmed.Hash }, Strategy = strategy, }); }
public async Task <BitcoinAddress> ReserveAddressAsync(DerivationStrategy derivationStrategy) { var client = _Client.GetExplorerClient(derivationStrategy.Network); var pathInfo = await client.GetUnusedAsync(derivationStrategy.DerivationStrategyBase, DerivationFeature.Deposit, 0, true).ConfigureAwait(false); return(pathInfo.ScriptPubKey.GetDestinationAddress(client.Network)); }
public async Task <Money> GetBalance(DerivationStrategy derivationStrategy) { var client = _Client.GetExplorerClient(derivationStrategy.Network); var result = await client.SyncAsync(derivationStrategy.DerivationStrategyBase, null, true); return(result.Confirmed.UTXOs.Select(u => u.Value) .Concat(result.Unconfirmed.UTXOs.Select(u => u.Value)) .Sum()); }
public async Task <IActionResult> WalletSend( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, string defaultDestination = null, string defaultAmount = null) { if (walletId?.StoreId == null) { return(NotFound()); } var store = await Repository.FindStore(walletId.StoreId, GetUserId()); DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store); if (paymentMethod == null) { return(NotFound()); } var storeData = store.GetStoreBlob(); var rateRules = store.GetStoreBlob().GetRateRules(NetworkProvider); rateRules.Spread = 0.0m; var currencyPair = new Rating.CurrencyPair(paymentMethod.PaymentId.CryptoCode, GetCurrencyCode(storeData.DefaultLang) ?? "USD"); WalletModel model = new WalletModel() { DefaultAddress = defaultDestination, DefaultAmount = defaultAmount, ServerUrl = GetLedgerWebsocketUrl(this.HttpContext, walletId.CryptoCode, paymentMethod.DerivationStrategyBase), CryptoCurrency = walletId.CryptoCode }; using (CancellationTokenSource cts = new CancellationTokenSource()) { try { cts.CancelAfter(TimeSpan.FromSeconds(5)); var result = await RateFetcher.FetchRate(currencyPair, rateRules).WithCancellation(cts.Token); if (result.BidAsk != null) { model.Rate = result.BidAsk.Center; model.Divisibility = _currencyTable.GetNumberFormatInfo(currencyPair.Right, true).CurrencyDecimalDigits; model.Fiat = currencyPair.Right; } else { model.RateError = $"{result.EvaluatedRule} ({string.Join(", ", result.Errors.OfType<object>().ToArray())})"; } } catch (Exception ex) { model.RateError = ex.Message; } } return(View(model)); }
public async Task <IActionResult> WalletRescan( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId) { if (walletId?.StoreId == null) { return(NotFound()); } var store = await Repository.FindStore(walletId.StoreId, GetUserId()); DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store); if (paymentMethod == null) { return(NotFound()); } var vm = new RescanWalletModel(); vm.IsFullySync = _dashboard.IsFullySynched(walletId.CryptoCode, out var unused); // We need to ensure it is segwit, // because hardware wallet support need the parent transactions to sign, which NBXplorer don't have. (Nor does a pruned node) vm.IsSegwit = paymentMethod.DerivationStrategyBase.IsSegwit(); vm.IsServerAdmin = User.Claims.Any(c => c.Type == Policies.CanModifyServerSettings.Key && c.Value == "true"); vm.IsSupportedByCurrency = _dashboard.Get(walletId.CryptoCode)?.Status?.BitcoinStatus?.Capabilities?.CanScanTxoutSet == true; var explorer = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode); var scanProgress = await explorer.GetScanUTXOSetInformationAsync(paymentMethod.DerivationStrategyBase); if (scanProgress != null) { vm.PreviousError = scanProgress.Error; if (scanProgress.Status == ScanUTXOStatus.Queued || scanProgress.Status == ScanUTXOStatus.Pending) { if (scanProgress.Progress == null) { vm.Progress = 0; } else { vm.Progress = scanProgress.Progress.OverallProgress; vm.RemainingTime = TimeSpan.FromSeconds(scanProgress.Progress.RemainingSeconds).PrettyPrint(); } } if (scanProgress.Status == ScanUTXOStatus.Complete) { vm.LastSuccess = scanProgress.Progress; vm.TimeOfScan = (scanProgress.Progress.CompletedAt.Value - scanProgress.Progress.StartedAt).PrettyPrint(); } } return(View(vm)); }
private IActionResult ShowAddresses(DerivationSchemeViewModel vm, DerivationStrategy strategy) { vm.DerivationScheme = strategy.DerivationStrategyBase.ToString(); if (!string.IsNullOrEmpty(vm.DerivationScheme)) { var line = strategy.DerivationStrategyBase.GetLineFor(DerivationFeature.Deposit); for (int i = 0; i < 10; i++) { var address = line.Derive((uint)i); vm.AddressSamples.Add((DerivationStrategyBase.GetKeyPath(DerivationFeature.Deposit).Derive((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(strategy.Network.NBitcoinNetwork).ToString())); } } vm.Confirmation = true; return(View(vm)); }
/// <summary> /// Submits transactions on the hash address. /// </summary> /// <param name="merkleRoot">The hash of the merkle root node</param> /// <param name="fundingKey">The server private key used for funding</param> /// <param name="previousTx">Output transaction from the last timestamp</param> /// <returns>The output transactions, can be used as input transaction for the next timestamp before confimation</returns> public IList <byte[]> Send(byte[] merkleRoot, byte[] fundingKey, IList <byte[]> previousTx = null) { var serverKey = new Key(fundingKey); var serverAddress = serverKey.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network); var txs = new List <byte[]>(); Key merkleRootKey = new Key(DerivationStrategy.GetKey(merkleRoot)); var fee = Repository.GetEstimatedFee().FeePerK; var coins = GetCoins(previousTx, fee, serverAddress); if (EnsureFee(fee, coins) > 0) { throw new ApplicationException("Not enough coin to spend."); } IDestination targetAddress = merkleRootKey.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network); var sourceTx = Network.CreateTransactionBuilder() .AddCoins(coins) .AddKeys(serverKey) .Send(targetAddress, fee) // Send to Batch address .SendFees(fee) .SetChange(serverAddress) .BuildTransaction(true); Repository.BroadcastAsync(sourceTx).GetAwaiter().GetResult(); txs.Add(sourceTx.ToBytes()); var txNota = Network.CreateTransactionBuilder() .AddCoins(sourceTx.Outputs.AsCoins()) .SendOP_Return(merkleRoot) // Put batch root on the OP_Return out tx .AddKeys(merkleRootKey) .SendFees(fee) .SetChange(serverAddress) .BuildTransaction(true); Repository.BroadcastAsync(txNota).GetAwaiter().GetResult(); txs.Add(txNota.ToBytes()); return(txs); }
public async Task <IActionResult> WalletSendLedger( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletSendLedgerModel vm) { if (walletId?.StoreId == null) { return(NotFound()); } var store = await Repository.FindStore(walletId.StoreId, GetUserId()); DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store); if (paymentMethod == null) { return(NotFound()); } var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode); if (network == null) { return(NotFound()); } return(View(vm)); }
public async Task <IActionResult> AddDerivationScheme(string storeId, DerivationSchemeViewModel vm, string cryptoCode) { vm.CryptoCode = cryptoCode; var store = HttpContext.GetStoreData(); if (store == null) { return(NotFound()); } var network = cryptoCode == null ? null : _ExplorerProvider.GetNetwork(cryptoCode); if (network == null) { return(NotFound()); } vm.RootKeyPath = network.GetRootKeyPath(); var wallet = _WalletProvider.GetWallet(network); if (wallet == null) { return(NotFound()); } PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike); var exisingStrategy = store.GetSupportedPaymentMethods(_NetworkProvider) .Where(c => c.PaymentId == paymentMethodId) .OfType <DerivationStrategy>() .Select(c => c.DerivationStrategyBase.ToString()) .FirstOrDefault(); DerivationStrategy strategy = null; try { if (!string.IsNullOrEmpty(vm.DerivationScheme)) { strategy = ParseDerivationStrategy(vm.DerivationScheme, null, network); vm.DerivationScheme = strategy.ToString(); } } catch { ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme"); vm.Confirmation = false; return(View(vm)); } var storeBlob = store.GetStoreBlob(); var wasExcluded = storeBlob.GetExcludedPaymentMethods().Match(paymentMethodId); var willBeExcluded = !vm.Enabled; var showAddress = // Show addresses if: // - If the user is testing the hint address in confirmation screen (vm.Confirmation && !string.IsNullOrWhiteSpace(vm.HintAddress)) || // - The user is setting a new derivation scheme (!vm.Confirmation && strategy != null && exisingStrategy != strategy.DerivationStrategyBase.ToString()) || // - The user is clicking on continue without changing anything (!vm.Confirmation && willBeExcluded == wasExcluded); showAddress = showAddress && strategy != null; if (!showAddress) { try { if (strategy != null) { await wallet.TrackAsync(strategy.DerivationStrategyBase); } store.SetSupportedPaymentMethod(paymentMethodId, strategy); storeBlob.SetExcluded(paymentMethodId, willBeExcluded); store.SetStoreBlob(storeBlob); } catch { ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme"); return(View(vm)); } await _Repo.UpdateStore(store); StatusMessage = $"Derivation scheme for {network.CryptoCode} has been modified."; return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId })); } else if (!string.IsNullOrEmpty(vm.HintAddress)) { BitcoinAddress address = null; try { address = BitcoinAddress.Create(vm.HintAddress, network.NBitcoinNetwork); } catch { ModelState.AddModelError(nameof(vm.HintAddress), "Invalid hint address"); return(ShowAddresses(vm, strategy)); } try { strategy = ParseDerivationStrategy(vm.DerivationScheme, address.ScriptPubKey, network); } catch { ModelState.AddModelError(nameof(vm.HintAddress), "Impossible to find a match with this address"); return(ShowAddresses(vm, strategy)); } vm.HintAddress = ""; vm.StatusMessage = "Address successfully found, please verify that the rest is correct and click on \"Confirm\""; ModelState.Remove(nameof(vm.HintAddress)); ModelState.Remove(nameof(vm.DerivationScheme)); } return(ShowAddresses(vm, strategy)); }
public async Task <IActionResult> WalletSend( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, string defaultDestination = null, string defaultAmount = null) { if (walletId?.StoreId == null) { return(NotFound()); } var store = await Repository.FindStore(walletId.StoreId, GetUserId()); DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store); if (paymentMethod == null) { return(NotFound()); } var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode); if (network == null) { return(NotFound()); } var storeData = store.GetStoreBlob(); var rateRules = store.GetStoreBlob().GetRateRules(NetworkProvider); rateRules.Spread = 0.0m; var currencyPair = new Rating.CurrencyPair(paymentMethod.PaymentId.CryptoCode, GetCurrencyCode(storeData.DefaultLang) ?? "USD"); WalletSendModel model = new WalletSendModel() { Destination = defaultDestination, CryptoCode = walletId.CryptoCode }; if (double.TryParse(defaultAmount, out var amount)) { model.Amount = (decimal)amount; } var feeProvider = _feeRateProvider.CreateFeeProvider(network); var recommendedFees = feeProvider.GetFeeRateAsync(); var balance = _walletProvider.GetWallet(network).GetBalance(paymentMethod.DerivationStrategyBase); model.CurrentBalance = (await balance).ToDecimal(MoneyUnit.BTC); model.RecommendedSatoshiPerByte = (int)(await recommendedFees).GetFee(1).Satoshi; model.FeeSatoshiPerByte = model.RecommendedSatoshiPerByte; using (CancellationTokenSource cts = new CancellationTokenSource()) { try { cts.CancelAfter(TimeSpan.FromSeconds(5)); var result = await RateFetcher.FetchRate(currencyPair, rateRules).WithCancellation(cts.Token); if (result.BidAsk != null) { model.Rate = result.BidAsk.Center; model.Divisibility = _currencyTable.GetNumberFormatInfo(currencyPair.Right, true).CurrencyDecimalDigits; model.Fiat = currencyPair.Right; } else { model.RateError = $"{result.EvaluatedRule} ({string.Join(", ", result.Errors.OfType<object>().ToArray())})"; } } catch (Exception ex) { model.RateError = ex.Message; } } return(View(model)); }
public async Task <IActionResult> AddDerivationScheme(string storeId, DerivationSchemeViewModel vm, string cryptoCode) { vm.ServerUrl = GetStoreUrl(storeId); vm.CryptoCode = cryptoCode; var store = HttpContext.GetStoreData(); if (store == null) { return(NotFound()); } var network = cryptoCode == null ? null : _ExplorerProvider.GetNetwork(cryptoCode); if (network == null) { return(NotFound()); } vm.RootKeyPath = network.GetRootKeyPath(); var wallet = _WalletProvider.GetWallet(network); if (wallet == null) { return(NotFound()); } PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike); DerivationStrategy strategy = null; try { if (!string.IsNullOrEmpty(vm.DerivationScheme)) { strategy = ParseDerivationStrategy(vm.DerivationScheme, null, network); vm.DerivationScheme = strategy.ToString(); } } catch { ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme"); vm.Confirmation = false; return(View(vm)); } if (!vm.Confirmation && strategy != null) { return(ShowAddresses(vm, strategy)); } if (vm.Confirmation && !string.IsNullOrWhiteSpace(vm.HintAddress)) { BitcoinAddress address = null; try { address = BitcoinAddress.Create(vm.HintAddress, network.NBitcoinNetwork); } catch { ModelState.AddModelError(nameof(vm.HintAddress), "Invalid hint address"); return(ShowAddresses(vm, strategy)); } try { strategy = ParseDerivationStrategy(vm.DerivationScheme, address.ScriptPubKey, network); } catch { ModelState.AddModelError(nameof(vm.HintAddress), "Impossible to find a match with this address"); return(ShowAddresses(vm, strategy)); } vm.HintAddress = ""; vm.StatusMessage = "Address successfully found, please verify that the rest is correct and click on \"Confirm\""; ModelState.Remove(nameof(vm.HintAddress)); ModelState.Remove(nameof(vm.DerivationScheme)); return(ShowAddresses(vm, strategy)); } else { try { if (strategy != null) { await wallet.TrackAsync(strategy.DerivationStrategyBase); } store.SetSupportedPaymentMethod(paymentMethodId, strategy); } catch { ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme"); return(View(vm)); } await _Repo.UpdateStore(store); StatusMessage = $"Derivation scheme for {network.CryptoCode} has been modified."; return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId })); } }
public async Task TrackAsync(DerivationStrategy derivationStrategy) { var client = _Client.GetExplorerClient(derivationStrategy.Network); await client.TrackAsync(derivationStrategy.DerivationStrategyBase); }
public async Task <IActionResult> AddDerivationScheme(string storeId, DerivationSchemeViewModel vm) { vm.ServerUrl = GetStoreUrl(storeId); var store = await _Repo.FindStore(storeId, GetUserId()); if (store == null) { return(NotFound()); } var network = vm.CryptoCurrency == null ? null : _ExplorerProvider.GetNetwork(vm.CryptoCurrency); vm.SetCryptoCurrencies(_ExplorerProvider, vm.CryptoCurrency); if (network == null) { ModelState.AddModelError(nameof(vm.CryptoCurrency), "Invalid network"); return(View(vm)); } var wallet = _WalletProvider.GetWallet(network); if (wallet == null) { ModelState.AddModelError(nameof(vm.CryptoCurrency), "Invalid network"); return(View(vm)); } PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike); DerivationStrategy strategy = null; try { if (!string.IsNullOrEmpty(vm.DerivationScheme)) { strategy = ParseDerivationStrategy(vm.DerivationScheme, vm.DerivationSchemeFormat, network); vm.DerivationScheme = strategy.ToString(); } store.SetSupportedPaymentMethod(paymentMethodId, strategy); } catch { ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme"); vm.Confirmation = false; return(View(vm)); } if (vm.Confirmation) { try { if (strategy != null) { await wallet.TrackAsync(strategy.DerivationStrategyBase); } store.SetSupportedPaymentMethod(paymentMethodId, strategy); } catch { ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme"); return(View(vm)); } await _Repo.UpdateStore(store); StatusMessage = $"Derivation scheme for {network.CryptoCode} has been modified."; return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId })); } else { if (!string.IsNullOrEmpty(vm.DerivationScheme)) { var line = strategy.DerivationStrategyBase.GetLineFor(DerivationFeature.Deposit); for (int i = 0; i < 10; i++) { var address = line.Derive((uint)i); vm.AddressSamples.Add((DerivationStrategyBase.GetKeyPath(DerivationFeature.Deposit).Derive((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork).ToString())); } } vm.Confirmation = true; return(View(vm)); } }