public async Task <ActionResult> Index(string search = null, int count = 5) { count = Math.Max(0, count); count = Math.Min(50, count); if (!string.IsNullOrWhiteSpace(search)) { search = search.Trim(); if (search.StartsWith("0x") || search.Contains("OP_")) { return(RedirectToAction("Address", new { address = search })); } try { BitcoinAddress.Create(search, QBit.Network); return(RedirectToAction("Address", new { address = search })); } catch { } if (search.Length == 32 * 2) { if (search.StartsWith("0000000000")) { return(RedirectToAction("Block", new { blockFeature = search })); } else { return(RedirectToAction("Transaction", new { txId = search })); } } try { BlockFeature.Parse(search); return(RedirectToAction("Block", new { blockFeature = search })); } catch { } return(View()); } var responses = await Task.WhenAll(Enumerable .Range(0, count) .Select(i => QBit.GetBlock(new BlockFeature(SpecialFeature.Last) { Offset = -i }, true, true)) .ToArray()); var model = new MainModel(); model.NextCount = count + 5; foreach (var response in responses.Where(r => r.ExtendedInformation != null && r.AdditionalInformation != null)) { var blockModel = new MainBlockModel(); blockModel.Hash = response.AdditionalInformation.BlockId; blockModel.Height = response.AdditionalInformation.Height; blockModel.Size = ToKB(response.ExtendedInformation.Size); blockModel.Time = ToRelative(response.AdditionalInformation.BlockTime); blockModel.TransactionsCount = response.ExtendedInformation.TransactionCount; blockModel.Fees = ToString(response.ExtendedInformation.BlockReward - response.ExtendedInformation.BlockSubsidy); model.Blocks.Add(blockModel); } return(View(model)); }
public static Transaction CreateFakeTx(Money coin, BitcoinAddress to) { return(CreateFakeTx(coin, (KeyId)to.Hash)); }
private List <TransactionInformation> QueryWithListReceivedByAddress(bool withProof, BitcoinAddress address) { var result = RPCClient.SendCommand("listreceivedbyaddress", 0, false, true, address.ToString()); var transactions = ((JArray)result.Result).OfType <JObject>().Select(o => o["txids"]).OfType <JArray>().SingleOrDefault(); if (transactions == null) { return(null); } HashSet <uint256> resultsSet = new HashSet <uint256>(); List <TransactionInformation> results = new List <TransactionInformation>(); foreach (var txIdObj in transactions) { var txId = new uint256(txIdObj.ToString()); //May have duplicates if (!resultsSet.Contains(txId)) { var tx = GetTransaction(txId); if (tx == null || (withProof && tx.Confirmations == 0)) { continue; } resultsSet.Add(txId); results.Add(tx); } } return(results); }
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.Network = network; vm.RootKeyPath = network.GetRootKeyPath(); DerivationSchemeSettings strategy = null; var wallet = _WalletProvider.GetWallet(network); if (wallet == null) { return(NotFound()); } if (!string.IsNullOrEmpty(vm.Config)) { if (!DerivationSchemeSettings.TryParseFromJson(vm.Config, network, out strategy)) { TempData.SetStatusMessageModel(new StatusMessageModel() { Severity = StatusMessageModel.StatusSeverity.Error, Message = "Config file was not in the correct format" }); vm.Confirmation = false; return(View(vm)); } } if (vm.ColdcardPublicFile != null) { if (!DerivationSchemeSettings.TryParseFromColdcard(await ReadAllText(vm.ColdcardPublicFile), network, out strategy)) { TempData.SetStatusMessageModel(new StatusMessageModel() { Severity = StatusMessageModel.StatusSeverity.Error, Message = "Coldcard public file was not in the correct format" }); vm.Confirmation = false; return(View(vm)); } } else { try { if (!string.IsNullOrEmpty(vm.DerivationScheme)) { var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, null, network); if (newStrategy.AccountDerivation != strategy?.AccountDerivation) { var accountKey = string.IsNullOrEmpty(vm.AccountKey) ? null : new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork); if (accountKey != null) { var accountSettings = newStrategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey); if (accountSettings != null) { accountSettings.AccountKeyPath = vm.KeyPath == null ? null : KeyPath.Parse(vm.KeyPath); accountSettings.RootFingerprint = string.IsNullOrEmpty(vm.RootFingerprint) ? (HDFingerprint?)null : new HDFingerprint(NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint)); } } strategy = newStrategy; strategy.Source = vm.Source; vm.DerivationScheme = strategy.AccountDerivation.ToString(); } } else { strategy = null; } } catch { ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme"); vm.Confirmation = false; return(View(vm)); } } var oldConfig = vm.Config; vm.Config = strategy == null ? null : strategy.ToJson(); PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike); var exisingStrategy = store.GetSupportedPaymentMethods(_NetworkProvider) .Where(c => c.PaymentId == paymentMethodId) .OfType <DerivationSchemeSettings>() .FirstOrDefault(); 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 clicking on continue after changing the config (!vm.Confirmation && oldConfig != vm.Config) || // - The user is clickingon continue without changing config nor enabling/disabling (!vm.Confirmation && oldConfig == vm.Config && willBeExcluded == wasExcluded); showAddress = showAddress && strategy != null; if (!showAddress) { try { if (strategy != null) { await wallet.TrackAsync(strategy.AccountDerivation); } 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); if (willBeExcluded != wasExcluded) { var label = willBeExcluded ? "disabled" : "enabled"; TempData[WellKnownTempData.SuccessMessage] = $"On-Chain payments for {network.CryptoCode} has been {label}."; } else { TempData[WellKnownTempData.SuccessMessage] = $"Derivation settings 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 { var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, address.ScriptPubKey, network); if (newStrategy.AccountDerivation != strategy.AccountDerivation) { strategy.AccountDerivation = newStrategy.AccountDerivation; strategy.AccountOriginal = null; } } catch { ModelState.AddModelError(nameof(vm.HintAddress), "Impossible to find a match with this address"); return(ShowAddresses(vm, strategy)); } vm.HintAddress = ""; TempData[WellKnownTempData.SuccessMessage] = "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<BitcoinSecret> DumpPrivKeyAsync(BitcoinAddress address) { var response = await SendCommandAsync("dumpprivkey", address.ToString()).ConfigureAwait(false); return Network.CreateFromBase58Data<BitcoinSecret>((string)response.Result); }
//https://en.bitcoin.it/wiki/List_of_address_prefixes public void CanDeduceNetworkInBase58Constructor() { BitcoinAddress addr = new BitcoinAddress("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"); Assert.Equal(addr.Network, Network.Main); }
public void base58_keys_valid_gen() { TestCase[] tests = TestCase.read_json(TestDataLocations.GetFileFromDataFolder("base58_keys_valid.json")); tests = tests.Concat(TestCase.read_json(TestDataLocations.GetFileFromDataFolder("base58_keys_valid2.json"))).ToArray(); Network network = null; foreach (TestCase test in tests) { string strTest = test.ToString(); if (test.Count < 3) // Allow for extra stuff (useful for comments) { Assert.False(true, "Bad test: " + strTest); continue; } string exp_base58string = (string)test[0]; byte[] exp_payload = TestUtils.ParseHex((string)test[1]); dynamic metadata = test.GetDynamic(2); bool isPrivkey = (bool)metadata.isPrivkey; bool isTestnet = (bool)metadata.isTestnet; if (isTestnet) { network = KnownNetworks.TestNet; } else { network = KnownNetworks.Main; } if (isPrivkey) { bool isCompressed = metadata.isCompressed; var key = new Key(exp_payload, fCompressedIn: isCompressed); BitcoinSecret secret = network.CreateBitcoinSecret(key); Assert.True(secret.ToString() == exp_base58string, "result mismatch: " + strTest); } else { string exp_addrType = (string)metadata.addrType; TxDestination dest; if (exp_addrType == "pubkey") { dest = new KeyId(new uint160(exp_payload)); } else if (exp_addrType == "script") { dest = new ScriptId(new uint160(exp_payload)); } else if (exp_addrType == "p2wpkh") { dest = new WitKeyId(new uint160(exp_payload)); } else if (exp_addrType == "p2wsh") { dest = new WitScriptId(exp_payload); } else if (exp_addrType == "none") { continue; } else { Assert.True(false, "Bad addrtype: " + strTest); continue; } try { BitcoinAddress addrOut = dest.GetAddress(network); Assert.True(addrOut.ToString() == exp_base58string, "mismatch: " + strTest); Assert.True(addrOut.ScriptPubKey == dest.ScriptPubKey); Assert.True(dest.ScriptPubKey.GetDestination(KnownNetworks.Main) == dest); } catch (ArgumentException) { Assert.True(dest.GetType() == typeof(TxDestination)); } } } }
public void ImportAddress(BitcoinAddress address, string label, bool rescan) { SendCommand("importaddress", address.ToString(), label, rescan); }
/// <summary> /// Take the first four bytes of SHA256(SHA256(generatedaddress)) and call it addresshash. /// </summary> /// <param name="address"></param> /// <returns></returns> internal static byte[] HashAddress(BitcoinAddress address) { return(Hashes.Hash256(Encoders.ASCII.DecodeData(address.ToString())).ToBytes().Take(4).ToArray()); }
public void CanComputeCrowdfundModel() { using (var tester = ServerTester.Create()) { tester.Start(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); user.ModifyStore(s => s.NetworkFeeMode = NetworkFeeMode.Never); var apps = user.GetController <AppsController>(); var vm = Assert.IsType <CreateAppViewModel>(Assert.IsType <ViewResult>(apps.CreateApp().Result).Model); vm.Name = "test"; vm.SelectedAppType = AppType.Crowdfund.ToString(); Assert.IsType <RedirectToActionResult>(apps.CreateApp(vm).Result); var appId = Assert.IsType <ListAppsViewModel>(Assert.IsType <ViewResult>(apps.ListApps().Result).Model) .Apps[0].Id; Logs.Tester.LogInformation("We create an invoice with a hardcap"); var crowdfundViewModel = Assert.IsType <UpdateCrowdfundViewModel>(Assert .IsType <ViewResult>(apps.UpdateCrowdfund(appId).Result).Model); crowdfundViewModel.Enabled = true; crowdfundViewModel.EndDate = null; crowdfundViewModel.TargetAmount = 100; crowdfundViewModel.TargetCurrency = "BTC"; crowdfundViewModel.UseAllStoreInvoices = true; crowdfundViewModel.EnforceTargetAmount = true; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); var anonAppPubsController = tester.PayTester.GetController <AppsPublicController>(); var publicApps = user.GetController <AppsPublicController>(); var model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); Assert.Equal(crowdfundViewModel.TargetAmount, model.TargetAmount); Assert.Equal(crowdfundViewModel.EndDate, model.EndDate); Assert.Equal(crowdfundViewModel.StartDate, model.StartDate); Assert.Equal(crowdfundViewModel.TargetCurrency, model.TargetCurrency); Assert.Equal(0m, model.Info.CurrentAmount); Assert.Equal(0m, model.Info.CurrentPendingAmount); Assert.Equal(0m, model.Info.ProgressPercentage); Logs.Tester.LogInformation("Unpaid invoices should show as pending contribution because it is hardcap"); Logs.Tester.LogInformation("Because UseAllStoreInvoices is true, we can manually create an invoice and it should show as contribution"); var invoice = user.BitPay.CreateInvoice(new Invoice() { Buyer = new Buyer() { email = "*****@*****.**" }, Price = 1m, Currency = "BTC", PosData = "posData", ItemDesc = "Some description", TransactionSpeed = "high", FullNotifications = true }, Facade.Merchant); model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); Assert.Equal(0m, model.Info.CurrentAmount); Assert.Equal(1m, model.Info.CurrentPendingAmount); Assert.Equal(0m, model.Info.ProgressPercentage); Assert.Equal(1m, model.Info.PendingProgressPercentage); Logs.Tester.LogInformation("Let's check current amount change once payment is confirmed"); var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network); tester.ExplorerNode.SendToAddress(invoiceAddress, invoice.BtcDue); tester.ExplorerNode.Generate(1); // By default invoice confirmed at 1 block TestUtils.Eventually(() => { model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); Assert.Equal(1m, model.Info.CurrentAmount); Assert.Equal(0m, model.Info.CurrentPendingAmount); }); Logs.Tester.LogInformation("Because UseAllStoreInvoices is true, let's make sure the invoice is tagged"); var invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult(); Assert.True(invoiceEntity.Version >= InvoiceEntity.InternalTagSupport_Version); Assert.Contains(AppService.GetAppInternalTag(appId), invoiceEntity.InternalTags); crowdfundViewModel.UseAllStoreInvoices = false; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); Logs.Tester.LogInformation("Because UseAllStoreInvoices is false, let's make sure the invoice is not tagged"); invoice = user.BitPay.CreateInvoice(new Invoice() { Buyer = new Buyer() { email = "*****@*****.**" }, Price = 1m, Currency = "BTC", PosData = "posData", ItemDesc = "Some description", TransactionSpeed = "high", FullNotifications = true }, Facade.Merchant); invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult(); Assert.DoesNotContain(AppService.GetAppInternalTag(appId), invoiceEntity.InternalTags); Logs.Tester.LogInformation("After turning setting a softcap, let's check that only actual payments are counted"); crowdfundViewModel.EnforceTargetAmount = false; crowdfundViewModel.UseAllStoreInvoices = true; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); invoice = user.BitPay.CreateInvoice(new Invoice() { Buyer = new Buyer() { email = "*****@*****.**" }, Price = 1m, Currency = "BTC", PosData = "posData", ItemDesc = "Some description", TransactionSpeed = "high", FullNotifications = true }, Facade.Merchant); Assert.Equal(0m, model.Info.CurrentPendingAmount); invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network); tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(0.5m)); tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(0.2m)); TestUtils.Eventually(() => { model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); Assert.Equal(0.7m, model.Info.CurrentPendingAmount); }); } }
protected SendControlViewModel(Wallet wallet, string title) : base(title) { Global = Locator.Current.GetService <Global>(); Wallet = wallet; LabelSuggestion = new SuggestLabelViewModel(); BuildTransactionButtonText = DoButtonText; ResetUi(); SetAmountWatermark(Money.Zero); CoinList = new CoinListViewModel(Wallet, displayCommonOwnershipWarning: true); Observable.FromEventPattern(CoinList, nameof(CoinList.SelectionChanged)) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => SetFeesAndTexts()); _minMaxFeeTargetsEqual = this.WhenAnyValue(x => x.MinimumFeeTarget, x => x.MaximumFeeTarget, (x, y) => x == y) .ToProperty(this, x => x.MinMaxFeeTargetsEqual, scheduler: RxApp.MainThreadScheduler); SetFeeTargetLimits(); FeeTarget = Global.UiConfig.FeeTarget; FeeDisplayFormat = (FeeDisplayFormat)(Enum.ToObject(typeof(FeeDisplayFormat), Global.UiConfig.FeeDisplayFormat) ?? FeeDisplayFormat.SatoshiPerByte); SetFeesAndTexts(); this.WhenAnyValue(x => x.AmountText) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(x => { if (Money.TryParse(x.TrimStart('~', ' '), out Money amountBtc)) { SetAmountWatermark(amountBtc); } else { SetAmountWatermark(Money.Zero); } SetFees(); }); AmountKeyUpCommand = ReactiveCommand.Create((KeyEventArgs key) => { var amount = AmountText; if (IsMax) { SetFeesAndTexts(); } else { // Correct amount Regex digitsOnly = new Regex(@"[^\d,.]"); string betterAmount = digitsOnly.Replace(amount, ""); // Make it digits , and . only. betterAmount = betterAmount.Replace(',', '.'); int countBetterAmount = betterAmount.Count(x => x == '.'); if (countBetterAmount > 1) // Do not enable typing two dots. { var index = betterAmount.IndexOf('.', betterAmount.IndexOf('.') + 1); if (index > 0) { betterAmount = betterAmount.Substring(0, index); } } var dotIndex = betterAmount.IndexOf('.'); if (dotIndex != -1 && betterAmount.Length - dotIndex > 8) // Enable max 8 decimals. { betterAmount = betterAmount.Substring(0, dotIndex + 1 + 8); } if (betterAmount != amount) { AmountText = betterAmount; } } }); this.WhenAnyValue(x => x.IsBusy, x => x.IsHardwareBusy) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => BuildTransactionButtonText = IsHardwareBusy ? WaitingForHardwareWalletButtonTextString : IsBusy ? DoingButtonText : DoButtonText); this.WhenAnyValue(x => x.FeeTarget) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => { IsSliderFeeUsed = true; SetFeesAndTexts(); }); this.WhenAnyValue(x => x.IsSliderFeeUsed) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(enabled => FeeControlOpacity = enabled ? 1 : 0.5); // Give the control the disabled feeling. Real Disable it not a solution as we have to detect if the slider is moved. MaxCommand = ReactiveCommand.Create(() => IsMax = !IsMax, outputScheduler: RxApp.MainThreadScheduler); this.WhenAnyValue(x => x.IsMax) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => { if (IsMax) { SetFeesAndTexts(); LabelToolTip = "Spending whole coins does not generate change, thus labeling is unnecessary."; } else { AmountText = "0.0"; LabelToolTip = "Who can link this transaction to you? E.g.: \"Max, BitPay\""; } }); // Triggering the detection of same address values. this.WhenAnyValue(x => x.Address) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => this.RaisePropertyChanged(nameof(CustomChangeAddress))); this.WhenAnyValue(x => x.CustomChangeAddress) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => this.RaisePropertyChanged(nameof(Address))); FeeRateCommand = ReactiveCommand.Create(ChangeFeeRateDisplay, outputScheduler: RxApp.MainThreadScheduler); OnAddressPasteCommand = ReactiveCommand.Create((BitcoinUrlBuilder url) => { SmartLabel label = url.Label; if (!label.IsEmpty) { LabelSuggestion.Label = label; } if (url.Amount != null) { AmountText = url.Amount.ToString(false, true); } }); BuildTransactionCommand = ReactiveCommand.CreateFromTask(async() => { try { IsBusy = true; MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.BuildingTransaction); var label = new SmartLabel(LabelSuggestion.Label); LabelSuggestion.Label = label; if (!IsMax && label.IsEmpty) { NotificationHelpers.Warning("Observers are required.", ""); return; } var selectedCoinViewModels = CoinList.Coins.Where(cvm => cvm.IsSelected); var selectedCoinReferences = selectedCoinViewModels.Select(cvm => cvm.Model.OutPoint).ToList(); if (!selectedCoinReferences.Any()) { NotificationHelpers.Warning("No coins are selected to spend.", ""); return; } BitcoinAddress address; try { address = BitcoinAddress.Create(Address, Global.Network); } catch (FormatException) { NotificationHelpers.Warning("Invalid address.", ""); return; } var requests = new List <DestinationRequest>(); if (Global.UiConfig.IsCustomChangeAddress && !IsMax && !string.IsNullOrWhiteSpace(CustomChangeAddress)) { try { var customChangeAddress = BitcoinAddress.Create(CustomChangeAddress, Global.Network); if (customChangeAddress == address) { NotificationHelpers.Warning("The active address and the change address cannot be the same.", ""); return; } requests.Add(new DestinationRequest(customChangeAddress, MoneyRequest.CreateChange(subtractFee: true), label)); } catch (FormatException) { NotificationHelpers.Warning("Invalid custom change address.", ""); return; } } MoneyRequest moneyRequest; if (IsMax) { moneyRequest = MoneyRequest.CreateAllRemaining(subtractFee: true); } else { if (!Money.TryParse(AmountText, out Money amount) || amount == Money.Zero) { NotificationHelpers.Warning("Invalid amount."); return; } if (amount == selectedCoinViewModels.Sum(x => x.Amount)) { NotificationHelpers.Warning("Looks like you want to spend whole coins. Try Max button instead.", ""); return; } moneyRequest = MoneyRequest.Create(amount, subtractFee: false); } if (FeeRate is null || FeeRate.SatoshiPerByte < 1) { NotificationHelpers.Warning("Invalid fee rate.", ""); return; } var feeStrategy = FeeStrategy.CreateFromFeeRate(FeeRate); var activeDestinationRequest = new DestinationRequest(address, moneyRequest, label); requests.Add(activeDestinationRequest); var intent = new PaymentIntent(requests); try { MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.DequeuingSelectedCoins); OutPoint[] toDequeue = selectedCoinViewModels.Where(x => x.CoinJoinInProgress).Select(x => x.Model.OutPoint).ToArray(); if (toDequeue != null && toDequeue.Any()) { await Wallet.ChaumianClient.DequeueCoinsFromMixAsync(toDequeue, DequeueReason.TransactionBuilding); } } catch { NotificationHelpers.Error("Cannot spend mixing coins.", ""); return; } finally { MainWindowViewModel.Instance.StatusBar.TryRemoveStatus(StatusType.DequeuingSelectedCoins); } if (!Wallet.KeyManager.IsWatchOnly) { try { PasswordHelper.GetMasterExtKey(Wallet.KeyManager, Password, out string compatiblityPasswordUsed); // We could use TryPassword but we need the exception. if (compatiblityPasswordUsed != null) { Password = compatiblityPasswordUsed; // Overwrite the password for BuildTransaction function. NotificationHelpers.Warning(PasswordHelper.CompatibilityPasswordWarnMessage); } } catch (SecurityException ex) { NotificationHelpers.Error(ex.Message, ""); return; } catch (Exception ex) { Logger.LogError(ex); NotificationHelpers.Error(ex.ToUserFriendlyString()); return; } } BuildTransactionResult result = await Task.Run(() => Wallet.BuildTransaction(Password, intent, feeStrategy, allowUnconfirmed: true, allowedInputs: selectedCoinReferences)); await DoAfterBuildTransaction(result); } catch (InsufficientBalanceException ex) { Money needed = ex.Minimum - ex.Actual; NotificationHelpers.Error($"Not enough coins selected. You need an estimated {needed.ToString(false, true)} BTC more to make this transaction.", ""); } catch (HttpRequestException ex) { NotificationHelpers.Error(ex.ToUserFriendlyString()); Logger.LogError(ex); } catch (Exception ex) { NotificationHelpers.Error(ex.ToUserFriendlyString(), sender: Wallet); Logger.LogError(ex); } finally { MainWindowViewModel.Instance.StatusBar.TryRemoveStatus(StatusType.BuildingTransaction, StatusType.SigningTransaction, StatusType.BroadcastingTransaction); IsBusy = false; } }, this.WhenAny(x => x.IsMax, x => x.AmountText, x => x.Address, x => x.IsBusy, (isMax, amount, address, busy) => (isMax.Value || !string.IsNullOrWhiteSpace(amount.Value)) && !string.IsNullOrWhiteSpace(Address) && !IsBusy) .ObserveOn(RxApp.MainThreadScheduler)); UserFeeTextKeyUpCommand = ReactiveCommand.Create((KeyEventArgs key) => { IsSliderFeeUsed = !IsCustomFee; SetFeesAndTexts(); }); FeeSliderClickedCommand = ReactiveCommand.Create((PointerPressedEventArgs mouse) => IsSliderFeeUsed = true); HighLightFeeSliderCommand = ReactiveCommand.Create((bool entered) => { if (IsSliderFeeUsed) { return; } FeeControlOpacity = entered ? 0.8 : 0.5; }); Observable .Merge(MaxCommand.ThrownExceptions) .Merge(FeeRateCommand.ThrownExceptions) .Merge(OnAddressPasteCommand.ThrownExceptions) .Merge(BuildTransactionCommand.ThrownExceptions) .Merge(UserFeeTextKeyUpCommand.ThrownExceptions) .Merge(FeeSliderClickedCommand.ThrownExceptions) .Merge(HighLightFeeSliderCommand.ThrownExceptions) .Merge(AmountKeyUpCommand.ThrownExceptions) .ObserveOn(RxApp.TaskpoolScheduler) .Subscribe(ex => { NotificationHelpers.Error(ex.ToUserFriendlyString()); Logger.LogError(ex); }); }
public void CanVerifySignature() { var tests = new[] { new { Address = "15jZVzLc9cXz5PUFFda5A4Z7kZDYPg2NnL", PrivateKey = "L3TiCqmvPkXJpzCCZJuhy6wQtJZWDkR1AuqFY4Utib5J5XLuvLdZ", Message = "This is an example of a signed message.", Signature = "H6sliOnVrD9r+J8boZAKHZwBIW2zLiD72IfTIF94bfZhBI0JdMu9AM9rrF7P6eH+866YvM4H9xWGVN4jMJZycFU=" }, new { Address = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ", PrivateKey = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj", Message = "hello world", Signature = "G+dnSEywl3v1ijlWXvpY6zpu+AKNNXJcVmrdE35m0mMlzwFzXDiNg+uZrG9k8mpQL6sjHKrlBoDNSA+yaPW7PEA=" }, new { Address = "1Q1wVsNNiUo68caU7BfyFFQ8fVBqxC2DSc", PrivateKey = null as string, Message = "Localbitcoins.com will change the world", Signature = "IJ/17TjGGUqmEppAliYBUesKHoHzfY4gR4DW0Yg7QzrHUB5FwX1uTJ/H21CF8ncY8HHNB5/lh8kPAOeD5QxV8Xc=" }, new { Address = "1GvPJp7H8UYsYDvE4GFoV4f2gSCNZzGF48", PrivateKey = "5JEeah4w29axvf5Yg9v9PKv86zcCN9qVbizJDMHmiSUxBqDFoUT", Message = "This is an example of a signed message2", Signature = "G8YNwlo+I36Ct+hZKGSBFl3q8Kbx1pxPpwQmwdsG85io76+DUOHXqh/DfBq+Cn2R3C3dI//g3koSjxy7yNxJ9m8=" }, new { Address = "1GvPJp7H8UYsYDvE4GFoV4f2gSCNZzGF48", PrivateKey = "5JEeah4w29axvf5Yg9v9PKv86zcCN9qVbizJDMHmiSUxBqDFoUT", Message = "this is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long message", Signature = "HFKBHewleUsotk6fWG0OvWS/E2pP4o5hixdD6ui60in/x4376FBI4DvtJYrljXLNJTG1pBOZG+qRT/7S9WiIBfQ=" }, new { Address = "bc1q463gmsagg5u8wvqqcqj92yytt0pmevvg39h9jp", PrivateKey = "5JEeah4w29axvf5Yg9v9PKv86zcCN9qVbizJDMHmiSUxBqDFoUT", Message = "this is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long message", Signature = "HFKBHewleUsotk6fWG0OvWS/E2pP4o5hixdD6ui60in/x4376FBI4DvtJYrljXLNJTG1pBOZG+qRT/7S9WiIBfQ=" }, new { // p2wpkh Address = "bc1q463gmsagg5u8wvqqcqj92yytt0pmevvg39h9jp", PrivateKey = "5JEeah4w29axvf5Yg9v9PKv86zcCN9qVbizJDMHmiSUxBqDFoUT", Message = "this is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long message", Signature = "HFKBHewleUsotk6fWG0OvWS/E2pP4o5hixdD6ui60in/x4376FBI4DvtJYrljXLNJTG1pBOZG+qRT/7S9WiIBfQ=" }, new { // p2wsh Address = "bc1qrr8fncdd8gsxajghfcy2upq37dvvc84t285g4lvfak9nrkqsalds9ms6qa", PrivateKey = "5JEeah4w29axvf5Yg9v9PKv86zcCN9qVbizJDMHmiSUxBqDFoUT", Message = "this is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long messagethis is a very long message", Signature = "HFKBHewleUsotk6fWG0OvWS/E2pP4o5hixdD6ui60in/x4376FBI4DvtJYrljXLNJTG1pBOZG+qRT/7S9WiIBfQ=" }, }; foreach (var test in tests) { var address = BitcoinAddress.Create(test.Address, Network.Main); var pkh = (address as IPubkeyHashUsable); if (test.PrivateKey != null) { var secret = Network.Main.CreateBitcoinSecret(test.PrivateKey); var signature = secret.PrivateKey.SignMessage(test.Message); Assert.True(pkh.VerifyMessage(test.Message, signature)); Assert.True(secret.PubKey.VerifyMessage(test.Message, signature)); } Assert.True(pkh.VerifyMessage(test.Message, test.Signature)); Assert.True(!pkh.VerifyMessage("bad message", test.Signature)); } }
public Program() { paymentSecret = new BitcoinSecret("L5W89cAuSXoyzdY1yTyTTX8B3EHDbrVpWyk5T197eoyngvgczbAi"); paymentAddress = paymentSecret.GetAddress(); }
public async Task <IActionResult> LedgerConnection( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, string command, // getinfo // getxpub int account = 0, // sendtoaddress string psbt = null, string hintChange = null ) { if (!HttpContext.WebSockets.IsWebSocketRequest) { return(NotFound()); } var network = NetworkProvider.GetNetwork <BTCPayNetwork>(walletId.CryptoCode); if (network == null) { throw new FormatException("Invalid value for crypto code"); } var storeData = (await Repository.FindStore(walletId.StoreId, GetUserId())); var derivationSettings = GetDerivationSchemeSettings(walletId, storeData); var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); using (var normalOperationTimeout = new CancellationTokenSource()) using (var signTimeout = new CancellationTokenSource()) { normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30)); var hw = new LedgerHardwareWalletService(webSocket); var model = new WalletSendLedgerModel(); object result = null; try { if (command == "test") { result = await hw.Test(normalOperationTimeout.Token); } if (command == "sendtoaddress") { if (!_dashboard.IsFullySynched(network.CryptoCode, out var summary)) { throw new Exception($"{network.CryptoCode}: not started or fully synched"); } var accountKey = derivationSettings.GetSigningAccountKeySettings(); // Some deployment does not have the AccountKeyPath set, let's fix this... if (accountKey.AccountKeyPath == null) { // If the saved wallet key path is not present or incorrect, let's scan the wallet to see if it can sign strategy var foundKeyPath = await hw.FindKeyPathFromDerivation(network, derivationSettings.AccountDerivation, normalOperationTimeout.Token); accountKey.AccountKeyPath = foundKeyPath ?? throw new HardwareWalletException($"This store is not configured to use this ledger"); storeData.SetSupportedPaymentMethod(derivationSettings); await Repository.UpdateStore(storeData); } // If it has already the AccountKeyPath, we did not looked up for it, so we need to check if we are on the right ledger else { // Checking if ledger is right with the RootFingerprint is faster as it does not need to make a query to the parent xpub, // but some deployment does not have it, so let's use AccountKeyPath instead if (accountKey.RootFingerprint == null) { var actualPubKey = await hw.GetExtPubKey(network, accountKey.AccountKeyPath, normalOperationTimeout.Token); if (!derivationSettings.AccountDerivation.GetExtPubKeys().Any(p => p.GetPublicKey() == actualPubKey.GetPublicKey())) { throw new HardwareWalletException($"This store is not configured to use this ledger"); } } // We have the root fingerprint, we can check the root from it else { var actualPubKey = await hw.GetPubKey(network, new KeyPath(), normalOperationTimeout.Token); if (actualPubKey.GetHDFingerPrint() != accountKey.RootFingerprint.Value) { throw new HardwareWalletException($"This store is not configured to use this ledger"); } } } // Some deployment does not have the RootFingerprint set, let's fix this... if (accountKey.RootFingerprint == null) { accountKey.RootFingerprint = (await hw.GetPubKey(network, new KeyPath(), normalOperationTimeout.Token)).GetHDFingerPrint(); storeData.SetSupportedPaymentMethod(derivationSettings); await Repository.UpdateStore(storeData); } var psbtResponse = new CreatePSBTResponse() { PSBT = PSBT.Parse(psbt, network.NBitcoinNetwork), ChangeAddress = string.IsNullOrEmpty(hintChange) ? null : BitcoinAddress.Create(hintChange, network.NBitcoinNetwork) }; derivationSettings.RebaseKeyPaths(psbtResponse.PSBT); signTimeout.CancelAfter(TimeSpan.FromMinutes(5)); psbtResponse.PSBT = await hw.SignTransactionAsync(psbtResponse.PSBT, accountKey.GetRootedKeyPath(), accountKey.AccountKey, psbtResponse.ChangeAddress?.ScriptPubKey, signTimeout.Token); result = new SendToAddressResult() { PSBT = psbtResponse.PSBT.ToBase64() }; } } catch (OperationCanceledException) { result = new LedgerTestResult() { Success = false, Error = "Timeout" }; } catch (Exception ex) { result = new LedgerTestResult() { Success = false, Error = ex.Message }; } finally { hw.Dispose(); } try { if (result != null) { UTF8Encoding UTF8NOBOM = new UTF8Encoding(false); var bytes = UTF8NOBOM.GetBytes(JsonConvert.SerializeObject(result, _mvcJsonOptions.Value.SerializerSettings)); await webSocket.SendAsync(new ArraySegment <byte>(bytes), WebSocketMessageType.Text, true, new CancellationTokenSource(2000).Token); } } catch { } finally { await webSocket.CloseSocket(); } } return(new EmptyResult()); }
public void base58_keys_valid_parse() { TestCase[] tests = TestCase.read_json(TestDataLocations.GetFileFromDataFolder("base58_keys_valid.json")); Network network; foreach (TestCase test in tests) { string strTest = test.ToString(); if (test.Count < 3) // Allow for extra stuff (useful for comments) { Assert.True(false, "Bad test " + strTest); continue; } string exp_base58string = (string)test[0]; byte[] exp_payload = TestUtils.ParseHex((string)test[1]); //const Object &metadata = test[2].get_obj(); bool isPrivkey = (bool)test.GetDynamic(2).isPrivkey; bool isTestnet = (bool)test.GetDynamic(2).isTestnet; if (isTestnet) { network = KnownNetworks.TestNet; } else { network = KnownNetworks.Main; } if (isPrivkey) { bool isCompressed = (bool)test.GetDynamic(2).isCompressed; // Must be valid private key // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not! BitcoinSecret secret = network.CreateBitcoinSecret(exp_base58string); //If not valid exception would throw Key privkey = secret.PrivateKey; Assert.True(privkey.IsCompressed == isCompressed, "compressed mismatch:" + strTest); Assert.True(Utils.ArrayEqual(privkey.ToBytes(), exp_payload), "key mismatch:" + strTest); // Private key must be invalid public key Assert.Throws <FormatException>(() => network.CreateBitcoinAddress(exp_base58string)); } else { string exp_addrType = (string)test.GetDynamic(2).addrType; // "script" or "pubkey" // Must be valid public key BitcoinAddress addr = network.CreateBitcoinAddress(exp_base58string); Assert.True((addr is BitcoinScriptAddress) == (exp_addrType == "script"), "isScript mismatch" + strTest); if (exp_addrType == "script") { Assert.True(addr.GetType() == typeof(BitcoinScriptAddress)); } if (exp_addrType == "pubkey") { Assert.True(addr.GetType() == typeof(BitcoinPubKeyAddress)); } Assert.Throws <FormatException>(() => network.CreateBitcoinSecret(exp_base58string)); } } }
/// <summary> /// Requires wallet support. Requires an unlocked wallet or an unencrypted wallet. /// </summary> /// <param name="address">A P2PKH or P2SH address to which the bitcoins should be sent</param> /// <param name="amount">The amount to spend</param> /// <param name="commentTx">A locally-stored (not broadcast) comment assigned to this transaction. Default is no comment</param> /// <param name="commentDest">A locally-stored (not broadcast) comment assigned to this transaction. Meant to be used for describing who the payment was sent to. Default is no comment</param> /// <returns>The TXID of the sent transaction</returns> public async Task<uint256> SendToAddressAsync(BitcoinAddress address, Money amount, string commentTx = null, string commentDest = null) { List<object> parameters = new List<object>(); parameters.Add(address.ToString()); parameters.Add(amount.ToString()); if(commentTx != null) parameters.Add(commentTx); if(commentDest != null) parameters.Add(commentDest); var resp = await SendCommandAsync(RPCOperations.sendtoaddress, parameters.ToArray()).ConfigureAwait(false); return uint256.Parse(resp.Result.ToString()); }
public Transaction SendWallMessage(string sourceTxId, string sourcePublicAddress, string stratPrivateKey, string destinationAddress, double amountTx, double feeTx, byte[] bytesMsg) { // RPC Connection to Stratis Blockchain var rpc = GetRPC(); // Can either use the raw transaction hex from a wallet's getrawtransaction CLI command, or look up the equivalent information via RPC Transaction tx = rpc.GetRawTransaction(uint256.Parse(sourceTxId)); // The destination address will receive 0.0001 STRAT BitcoinAddress destAddress = BitcoinAddress.Create(destinationAddress, Network.StratisMain); // Stratis Source Address - The source address is used to store the 'change' from the transaction - THIS IS THE SIGNATURE for the attestation BitcoinAddress sourceAddress = BitcoinAddress.Create(sourcePublicAddress, Network.StratisMain); // The private key must be the key for the source address to be able to send funds from the source address (String of ~52 ASCII) BitcoinSecret sourcePrivateKey = new BitcoinSecret(stratPrivateKey, Network.StratisMain); int outputIndex = 0; int indexTx = 0; TxOutList listOutTx = tx.Outputs; foreach (var item in listOutTx) { string opCode = item.ScriptPubKey.ToString(); if (opCode.StartsWith("OP_DUP OP_HASH160")) { string sAddress = new Script(opCode).GetDestinationAddress(Network.StratisMain).ToString(); if (sAddress.Equals(sourcePublicAddress)) { outputIndex = indexTx; } } ++indexTx; } // For the fee to be correctly calculated, the quantity of funds in the source transaction needs to be known Money remainingBalance = tx.Outputs[outputIndex].Value; // Now that the source Transaction is obtained, the right output needs to be selected as an input for the new transaction. OutPoint outPoint = new OutPoint(tx, outputIndex); // The source transaction's output (must be unspent) is the input for the new transaction Transaction sendTx = new Transaction(); sendTx.Inputs.Add(new TxIn() { PrevOut = outPoint }); // Can currently only send a maximum of 40 bytes in the null data transaction (bytesMsg) // Also note that a nulldata transaction currently has to have a nonzero value assigned to it. TxOut messageTxOut = new TxOut() { Value = new Money((decimal)0.0001, MoneyUnit.BTC), ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(bytesMsg) }; // For Attestation amountTx = 0.0001 STRAT is being sent to destAddress TxOut destTxOut = new TxOut() { Value = new Money((decimal)amountTx, MoneyUnit.BTC), ScriptPubKey = destAddress.ScriptPubKey }; double discBalance = feeTx + amountTx + 0.0001; // 0.0001 : nulldata transaction amount // This is what subsequent transactions use to prove ownership of the funds (more specifically, the private key used to create the ScriptPubKey is known) // Send the change back to the originating address. TxOut changeBackTxOut = new TxOut() { Value = new Money(((remainingBalance.ToDecimal(MoneyUnit.BTC) - (decimal)discBalance)), MoneyUnit.BTC), ScriptPubKey = sourceAddress.ScriptPubKey }; // changeBackTxOut = remainingBalance - 0.0001 (sent) - 0.0001 (network fee) - 0.0001 (nulldata) // Transactions without fees may violate consensus rules, or may not be relayed by other nodes on the network. // Add the outputs to the transaction being built sendTx.Outputs.Add(destTxOut); sendTx.Outputs.Add(messageTxOut); sendTx.Outputs.Add(changeBackTxOut); // Signing the transaction sendTx.Inputs[0].ScriptSig = sourceAddress.ScriptPubKey; // Sign the transaction using the specified private key sendTx.Sign(sourcePrivateKey, false); // Broadcast Transaction rpc.SendRawTransactionAsync(sendTx); return(sendTx); }
public async Task ImportAddressAsync(BitcoinAddress address, string label, bool rescan) { await SendCommandAsync("importaddress", address.ToString(), label, rescan).ConfigureAwait(false); }
public async Task <IActionResult> LedgerConnection( [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, string command, // getinfo // getxpub int account = 0, // sendtoaddress bool noChange = false, string destination = null, string amount = null, string feeRate = null, string substractFees = null ) { if (!HttpContext.WebSockets.IsWebSocketRequest) { return(NotFound()); } var cryptoCode = walletId.CryptoCode; var storeData = (await Repository.FindStore(walletId.StoreId, GetUserId())); var derivationScheme = GetPaymentMethod(walletId, storeData).DerivationStrategyBase; var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); using (var normalOperationTimeout = new CancellationTokenSource()) using (var signTimeout = new CancellationTokenSource()) { normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30)); var hw = new HardwareWalletService(webSocket); object result = null; try { BTCPayNetwork network = null; if (cryptoCode != null) { network = NetworkProvider.GetNetwork(cryptoCode); if (network == null) { throw new FormatException("Invalid value for crypto code"); } } BitcoinAddress destinationAddress = null; if (destination != null) { try { destinationAddress = BitcoinAddress.Create(destination.Trim(), network.NBitcoinNetwork); } catch { } if (destinationAddress == null) { throw new FormatException("Invalid value for destination"); } } FeeRate feeRateValue = null; if (feeRate != null) { try { feeRateValue = new FeeRate(Money.Satoshis(int.Parse(feeRate, CultureInfo.InvariantCulture)), 1); } catch { } if (feeRateValue == null || feeRateValue.FeePerK <= Money.Zero) { throw new FormatException("Invalid value for fee rate"); } } Money amountBTC = null; if (amount != null) { try { amountBTC = Money.Parse(amount); } catch { } if (amountBTC == null || amountBTC <= Money.Zero) { throw new FormatException("Invalid value for amount"); } } bool subsctractFeesValue = false; if (substractFees != null) { try { subsctractFeesValue = bool.Parse(substractFees); } catch { throw new FormatException("Invalid value for subtract fees"); } } if (command == "test") { result = await hw.Test(normalOperationTimeout.Token); } if (command == "sendtoaddress") { if (!_dashboard.IsFullySynched(network.CryptoCode, out var summary)) { throw new Exception($"{network.CryptoCode}: not started or fully synched"); } var strategy = GetDirectDerivationStrategy(derivationScheme); var wallet = _walletProvider.GetWallet(network); var change = wallet.GetChangeAddressAsync(derivationScheme); var keypaths = new Dictionary <Script, KeyPath>(); List <Coin> availableCoins = new List <Coin>(); foreach (var c in await wallet.GetUnspentCoins(derivationScheme)) { keypaths.TryAdd(c.Coin.ScriptPubKey, c.KeyPath); availableCoins.Add(c.Coin); } var changeAddress = await change; var storeBlob = storeData.GetStoreBlob(); var paymentId = new Payments.PaymentMethodId(cryptoCode, Payments.PaymentTypes.BTCLike); var foundKeyPath = storeBlob.GetWalletKeyPathRoot(paymentId); // Some deployment have the wallet root key path saved in the store blob // If it does, we only have to make 1 call to the hw to check if it can sign the given strategy, if (foundKeyPath == null || !await hw.CanSign(network, strategy, foundKeyPath, normalOperationTimeout.Token)) { // If the saved wallet key path is not present or incorrect, let's scan the wallet to see if it can sign strategy foundKeyPath = await hw.FindKeyPath(network, strategy, normalOperationTimeout.Token); if (foundKeyPath == null) { throw new HardwareWalletException($"This store is not configured to use this ledger"); } storeBlob.SetWalletKeyPathRoot(paymentId, foundKeyPath); storeData.SetStoreBlob(storeBlob); await Repository.UpdateStore(storeData); } retry: var send = new[] { (
private static Transaction CreateSpecTransaction(NoSqlColoredTransactionRepository repo, Money dust, BitcoinAddress receiver, Transaction prior, Transaction issuanceA1, Transaction issuanceA2) { var testedTx = new Transaction(); testedTx.Inputs.Add(new TxIn(new OutPoint(issuanceA1.GetHash(), 0))); testedTx.Inputs.Add(new TxIn(new OutPoint(issuanceA1.GetHash(), 1))); testedTx.Inputs.Add(new TxIn(new OutPoint(prior.GetHash(), 0))); testedTx.Inputs.Add(new TxIn(new OutPoint(issuanceA1.GetHash(), 2))); testedTx.Inputs.Add(new TxIn(new OutPoint(issuanceA1.GetHash(), 3))); testedTx.Inputs.Add(new TxIn(new OutPoint(issuanceA2.GetHash(), 0))); testedTx.Outputs.Add(new TxOut(Money.Parse("0.6"), receiver)); testedTx.Outputs.Add(new TxOut(dust, receiver)); testedTx.Outputs.Add(new TxOut(dust, new ColorMarker(new ulong[] { 0, 10, 6, 0, 7, 3 }).GetScript())); testedTx.Outputs.Add(new TxOut(dust, receiver)); testedTx.Outputs.Add(new TxOut(dust, receiver)); testedTx.Outputs.Add(new TxOut(dust, receiver)); testedTx.Outputs.Add(new TxOut(dust, receiver)); repo.Transactions.Put(testedTx.GetHash(), testedTx); return testedTx; }
public UnspentCoinModel[] ListUnspent(int minConfirmations = 1, int maxConfirmations = 9999999, string addressesJson = null) { List <BitcoinAddress> addresses = new List <BitcoinAddress>(); if (!string.IsNullOrEmpty(addressesJson)) { JsonConvert.DeserializeObject <List <string> >(addressesJson).ForEach(i => addresses.Add(BitcoinAddress.Create(i, this.FullNode.Network))); } WalletAccountReference accountReference = this.GetWalletAccountReference(); IEnumerable <UnspentOutputReference> spendableTransactions = this.walletManager.GetSpendableTransactionsInAccount(accountReference, minConfirmations); var unspentCoins = new List <UnspentCoinModel>(); foreach (var spendableTx in spendableTransactions) { if (spendableTx.Confirmations <= maxConfirmations) { if (!addresses.Any() || addresses.Contains(BitcoinAddress.Create(spendableTx.Address.Address, this.FullNode.Network))) { unspentCoins.Add(new UnspentCoinModel() { Account = accountReference.AccountName, Address = spendableTx.Address.Address, Id = spendableTx.Transaction.Id, Index = spendableTx.Transaction.Index, Amount = spendableTx.Transaction.Amount, ScriptPubKeyHex = spendableTx.Transaction.ScriptPubKey.ToHex(), RedeemScriptHex = null, // TODO: Currently don't support P2SH wallet addresses, review if we do. Confirmations = spendableTx.Confirmations, IsSpendable = !spendableTx.Transaction.IsSpent(), IsSolvable = !spendableTx.Transaction.IsSpent() // If it's spendable we assume it's solvable. }); } } } return(unspentCoins.ToArray()); }
static void Main(string[] args) { Network network = Network.TestNet; var treasurer = new BitcoinSecret("key") var alice = new BitcoinSecret("key"); Console.WriteLine("treasurer key: " + treasurer.PrivateKey.GetWif(network)); Console.WriteLine("Alice key: " + alice.PrivateKey.GetWif(network)); var scriptPubKey = PayToMultiSigTemplate .Instance .GenerateScriptPubKey(2, new[] { alice.PubKey, treasurer.PubKey }); Console.WriteLine("PubKey script: " + scriptPubKey); var redeemScript = PayToMultiSigTemplate .Instance .GenerateScriptPubKey(2, new[] { bob.PubKey, alice.PubKey, treasurer.PubKey }); var paymentScript = redeemScript.PaymentScript; Console.WriteLine("paymentScript: " + paymentScript); Console.WriteLine("multi-sig address: " + redeemScript.Hash.GetAddress(network)); var client = new QBitNinjaClient(network); // Update var receiveTransactionId = uint256.Parse("yourid"); var receiveTransactionResponse = client.GetTransaction(receiveTransactionId).Result; Console.WriteLine(receiveTransactionResponse.TransactionId); var receiveTransactionResponse = client.GetTransaction(receiveTransactionId).Result; Console.WriteLine(receiveTransactionResponse.TransactionId); Console.WriteLine(receiveTransactionResponse.Block.Confirmations); var receivedCoins = receiveTransactionResponse.ReceivedCoins; OutPoint outpointToSpend = null; ScriptCoin coinToSpend = null; foreach (var c in receivedCoins) { try { coinToSpend = new ScriptCoin(c.Outpoint, c.TxOut, redeemScript); outpointToSpend = c.Outpoint; break; } catch { } if (outpointToSpend == null) throw new Exception("TxOut doesn't contain any our ScriptPubKey"); Console.WriteLine("outpoint #{0}"); var lucasAddress = BitcoinAddress.Create("address", network); TransactionBuilder builder = network.CreateTransactionBuilder(); var minerFee = new Money(0.0005m, MoneyUnit.BTC); var txInAmount = (Money)receivedCoins[(int)outpointToSpend.N].Amount; var sendAmount = txInAmount - minerFee; Transaction unsigned = builder .AddCoins(coinToSpend) .Send(lucasAddress, sendAmount) .SetChange(lucasAddress, ChangeType.Uncolored) .BuildTransaction(sign: false); Transaction aliceSigned = builder .AddCoins(coinToSpend) .AddKeys(alice) Transaction bobSigned = builder .AddCoins(coinToSpend) .AddKeys(bob) .SignTransaction(aliceSigned);
public async Task <uint256> SendManyAsync(string fromAccount, string addressesJson, int minConf = 1, string comment = null, string subtractFeeFromJson = null, bool isReplaceable = false, int?confTarget = null, string estimateMode = "UNSET") { if (string.IsNullOrEmpty(addressesJson)) { throw new RPCServerException(RPCErrorCode.RPC_INVALID_PARAMETER, "No valid output addresses specified."); } var addresses = new Dictionary <string, decimal>(); try { // Outputs addresses are key-value pairs of address, amount. Translate to Receipient list. addresses = JsonConvert.DeserializeObject <Dictionary <string, decimal> >(addressesJson); } catch (JsonSerializationException ex) { throw new RPCServerException(RPCErrorCode.RPC_PARSE_ERROR, ex.Message); } if (addresses.Count == 0) { throw new RPCServerException(RPCErrorCode.RPC_INVALID_PARAMETER, "No valid output addresses specified."); } // Optional list of addresses to subtract fees from. IEnumerable <BitcoinAddress> subtractFeeFromAddresses = null; if (!string.IsNullOrEmpty(subtractFeeFromJson)) { try { subtractFeeFromAddresses = JsonConvert.DeserializeObject <List <string> >(subtractFeeFromJson).Select(i => BitcoinAddress.Create(i, this.FullNode.Network)); } catch (JsonSerializationException ex) { throw new RPCServerException(RPCErrorCode.RPC_PARSE_ERROR, ex.Message); } } var recipients = new List <Recipient>(); foreach (var address in addresses) { // Check for duplicate recipients var recipientAddress = BitcoinAddress.Create(address.Key, this.FullNode.Network).ScriptPubKey; if (recipients.Any(r => r.ScriptPubKey == recipientAddress)) { throw new RPCServerException(RPCErrorCode.RPC_INVALID_PARAMETER, string.Format("Invalid parameter, duplicated address: {0}.", recipientAddress)); } var recipient = new Recipient { ScriptPubKey = recipientAddress, Amount = Money.Coins(address.Value), SubtractFeeFromAmount = subtractFeeFromAddresses == null ? false : subtractFeeFromAddresses.Contains(BitcoinAddress.Create(address.Key, this.FullNode.Network)) }; recipients.Add(recipient); } WalletAccountReference accountReference = this.GetWalletAccountReference(); var context = new TransactionBuildContext(this.FullNode.Network) { AccountReference = accountReference, MinConfirmations = minConf, Shuffle = true, // We shuffle transaction outputs by default as it's better for anonymity. Recipients = recipients, CacheSecret = false }; // Set fee type for transaction build context. context.FeeType = FeeType.Medium; if (estimateMode.Equals("ECONOMICAL", StringComparison.InvariantCultureIgnoreCase)) { context.FeeType = FeeType.Low; } else if (estimateMode.Equals("CONSERVATIVE", StringComparison.InvariantCultureIgnoreCase)) { context.FeeType = FeeType.High; } try { // Log warnings for currently unsupported parameters. if (!string.IsNullOrEmpty(comment)) { this.logger.LogWarning("'comment' parameter is currently unsupported. Ignored."); } if (isReplaceable) { this.logger.LogWarning("'replaceable' parameter is currently unsupported. Ignored."); } if (confTarget != null) { this.logger.LogWarning("'conf_target' parameter is currently unsupported. Ignored."); } Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); await this.broadcasterManager.BroadcastTransactionAsync(transaction); return(transaction.GetHash()); } catch (SecurityException) { throw new RPCServerException(RPCErrorCode.RPC_WALLET_UNLOCK_NEEDED, "Wallet unlock needed"); } catch (WalletException exception) { throw new RPCServerException(RPCErrorCode.RPC_WALLET_ERROR, exception.Message); } catch (NotImplementedException exception) { throw new RPCServerException(RPCErrorCode.RPC_MISC_ERROR, exception.Message); } }
private List <TransactionInformation> Filter(RPCWalletEntry[] entries, bool includeUnconf, BitcoinAddress address) { List <TransactionInformation> results = new List <TransactionInformation>(); HashSet <uint256> resultsSet = new HashSet <uint256>(); foreach (var obj in entries) { //May have duplicates if (!resultsSet.Contains(obj.TransactionId)) { var confirmations = obj.Confirmations; var tx = _Cache.GetTransaction(obj.TransactionId); if (tx == null || (!includeUnconf && confirmations == 0)) { continue; } if (tx.Outputs.Any(o => o.ScriptPubKey == address.ScriptPubKey) || tx.Inputs.Any(o => o.ScriptSig.GetSigner().ScriptPubKey == address.ScriptPubKey)) { resultsSet.Add(obj.TransactionId); results.Add(new TransactionInformation() { Transaction = tx, Confirmations = confirmations }); } } } return(results); }
public uint256 SendToAddress(BitcoinAddress address, Money amount) { return(SendToAddressAsync(address, amount).GetAwaiter().GetResult()); }
/// <summary> /// Creates a cold staking withdrawal <see cref="Transaction"/>. /// </summary> /// <remarks> /// Cold staking withdrawal is performed on the wallet that is in the role of the cold staking cold wallet. /// </remarks> /// <param name="walletTransactionHandler">The wallet transaction handler used to build the transaction.</param> /// <param name="receivingAddress">The address that will receive the withdrawal.</param> /// <param name="walletName">The name of the wallet in the role of cold wallet.</param> /// <param name="walletPassword">The wallet password.</param> /// <param name="amount">The amount to remove from cold staking.</param> /// <param name="feeAmount">The fee to pay for cold staking transaction withdrawal.</param> /// <returns>The <see cref="Transaction"/> for cold staking withdrawal.</returns> /// <exception cref="WalletException">Thrown if the receiving address is in a cold staking account in this wallet.</exception> /// <exception cref="ArgumentNullException">Thrown if the receiving address is invalid.</exception> internal Transaction GetColdStakingWithdrawalTransaction(IWalletTransactionHandler walletTransactionHandler, string receivingAddress, string walletName, string walletPassword, Money amount, Money feeAmount) { Guard.NotEmpty(receivingAddress, nameof(receivingAddress)); Guard.NotEmpty(walletName, nameof(walletName)); Guard.NotNull(amount, nameof(amount)); Guard.NotNull(feeAmount, nameof(feeAmount)); this.logger.LogTrace("({0}:'{1}',{2}:'{3}',{4}:'{5}',{6}:'{7}'", nameof(receivingAddress), receivingAddress, nameof(walletName), walletName, nameof(amount), amount, nameof(feeAmount), feeAmount ); Wallet.Wallet wallet = this.GetWalletByName(walletName); // Get the cold staking account. HdAccount coldAccount = this.GetColdStakingAccount(wallet, true); if (coldAccount == null) { this.logger.LogTrace("(-)[COLDSTAKE_ACCOUNT_DOES_NOT_EXIST]"); throw new WalletException("The cold wallet account does not exist."); } // Prevent reusing cold stake addresses as regular withdrawal addresses. if (coldAccount.ExternalAddresses.Concat(coldAccount.InternalAddresses).Select(a => a.Address.ToString()).Contains(receivingAddress)) { this.logger.LogTrace("(-)[COLDSTAKE_INVALID_COLD_WALLET_ADDRESS_USAGE]"); throw new WalletException("You can't send the money to a cold staking cold wallet account."); } HdAccount hotAccount = this.GetColdStakingAccount(wallet, false); if (hotAccount != null && hotAccount.ExternalAddresses.Concat(hotAccount.InternalAddresses).Select(a => a.Address.ToString()).Contains(receivingAddress)) { this.logger.LogTrace("(-)[COLDSTAKE_INVALID_HOT_WALLET_ADDRESS_USAGE]"); throw new WalletException("You can't send the money to a cold staking hot wallet account."); } // Send the money to the receiving address. Script destination = BitcoinAddress.Create(receivingAddress, wallet.Network).ScriptPubKey; // Create the transaction build context (used in BuildTransaction). var accountReference = new WalletAccountReference(walletName, coldAccount.Name); var context = new TransactionBuildContext(wallet.Network) { AccountReference = accountReference, // Specify a dummy change address to prevent a change (internal) address from being created. // Will be changed after the transacton is built and before it is signed. ChangeAddress = coldAccount.ExternalAddresses.First(), TransactionFee = feeAmount, MinConfirmations = 0, Shuffle = false, Sign = false, Recipients = new[] { new Recipient { Amount = amount, ScriptPubKey = destination } }.ToList() }; // Register the cold staking builder extension with the transaction builder. context.TransactionBuilder.Extensions.Add(new ColdStakingBuilderExtension(false)); // Avoid script errors due to missing scriptSig. context.TransactionBuilder.StandardTransactionPolicy.ScriptVerify = null; // Build the transaction according to the settings recorded in the context. Transaction transaction = walletTransactionHandler.BuildTransaction(context); // Map OutPoint to UnspentOutputReference. Dictionary <OutPoint, UnspentOutputReference> mapOutPointToUnspentOutput = this.GetSpendableTransactionsInAccount(accountReference) .ToDictionary(unspent => unspent.ToOutPoint(), unspent => unspent); // Set the cold staking scriptPubKey on the change output. TxOut changeOutput = transaction.Outputs.SingleOrDefault(output => (output.ScriptPubKey != destination) && (output.Value != 0)); if (changeOutput != null) { // Find the largest input. TxIn largestInput = transaction.Inputs.OrderByDescending(input => mapOutPointToUnspentOutput[input.PrevOut].Transaction.Amount).Take(1).Single(); // Set the scriptPubKey of the change output to the scriptPubKey of the largest input. changeOutput.ScriptPubKey = mapOutPointToUnspentOutput[largestInput.PrevOut].Transaction.ScriptPubKey; } // Add keys for signing inputs. foreach (TxIn input in transaction.Inputs) { UnspentOutputReference unspent = mapOutPointToUnspentOutput[input.PrevOut]; context.TransactionBuilder.AddKeys(wallet.GetExtendedPrivateKeyForAddress(walletPassword, unspent.Address)); } // Sign the transaction. context.TransactionBuilder.SignTransactionInPlace(transaction); this.logger.LogTrace("(-):'{0}'", transaction.GetHash()); return(transaction); }
public async Task CanPlayWithPSBT() { using var tester = CreateServerTester(); await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); var invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 10, Currency = "USD", PosData = "posData", OrderId = "orderId", ItemDesc = "Some \", description", FullNotifications = true }, Facade.Merchant); var cashCow = tester.ExplorerNode; var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network); cashCow.SendToAddress(invoiceAddress, Money.Coins(1.5m)); TestUtils.Eventually(() => { invoice = user.BitPay.GetInvoice(invoice.Id); Assert.Equal("paid", invoice.Status); }); var walletController = user.GetController <UIWalletsController>(); var walletId = new WalletId(user.StoreId, "BTC"); var sendDestination = new Key().PubKey.Hash.GetAddress(user.SupportedNetwork.NBitcoinNetwork).ToString(); var sendModel = new WalletSendModel() { Outputs = new List <WalletSendModel.TransactionOutput>() { new WalletSendModel.TransactionOutput() { DestinationAddress = sendDestination, Amount = 0.1m, } }, FeeSatoshiPerByte = 1, CurrentBalance = 1.5m }; string redirectedPSBT = AssertRedirectedPSBT(await walletController.WalletSend(walletId, sendModel, command: "analyze-psbt"), nameof(walletController.WalletPSBT)); var vmPSBT = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel() { PSBT = redirectedPSBT }).AssertViewModelAsync <WalletPSBTViewModel>(); var unsignedPSBT = PSBT.Parse(vmPSBT.PSBT, user.SupportedNetwork.NBitcoinNetwork); Assert.NotNull(vmPSBT.Decoded); var filePSBT = (FileContentResult)(await walletController.WalletPSBT(walletId, vmPSBT, "save-psbt")); PSBT.Load(filePSBT.FileContents, user.SupportedNetwork.NBitcoinNetwork); var vmPSBT2 = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel { SigningContext = new SigningContextModel { PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady)) } }).AssertViewModelAsync <WalletPSBTViewModel>(); Assert.NotEmpty(vmPSBT2.Inputs.Where(i => i.Error != null)); Assert.Equal(vmPSBT.PSBT, vmPSBT2.SigningContext.PSBT); var signedPSBT = unsignedPSBT.Clone(); signedPSBT.SignAll(user.DerivationScheme, user.GenerateWalletResponseV.AccountHDKey, user.GenerateWalletResponseV.AccountKeyPath); vmPSBT.PSBT = signedPSBT.ToBase64(); var psbtReady = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel { SigningContext = new SigningContextModel { PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady)) } }).AssertViewModelAsync <WalletPSBTViewModel>(); Assert.Equal(2 + 1, psbtReady.Destinations.Count); // The fee is a destination Assert.Contains(psbtReady.Destinations, d => d.Destination == sendDestination && !d.Positive); Assert.Contains(psbtReady.Destinations, d => d.Positive); vmPSBT.PSBT = unsignedPSBT.ToBase64(); var combineVM = await walletController.WalletPSBT(walletId, vmPSBT, "combine").AssertViewModelAsync <WalletPSBTCombineViewModel>(); Assert.Equal(vmPSBT.PSBT, combineVM.OtherPSBT); combineVM.PSBT = signedPSBT.ToBase64(); var psbt = AssertRedirectedPSBT(await walletController.WalletPSBTCombine(walletId, combineVM), nameof(walletController.WalletPSBT)); var signedPSBT2 = PSBT.Parse(psbt, user.SupportedNetwork.NBitcoinNetwork); Assert.True(signedPSBT.TryFinalize(out _)); Assert.True(signedPSBT2.TryFinalize(out _)); Assert.Equal(signedPSBT, signedPSBT2); // Can use uploaded file? combineVM.PSBT = null; combineVM.UploadedPSBTFile = TestUtils.GetFormFile("signedPSBT", signedPSBT.ToBytes()); psbt = AssertRedirectedPSBT(await walletController.WalletPSBTCombine(walletId, combineVM), nameof(walletController.WalletPSBT)); signedPSBT2 = PSBT.Parse(psbt, user.SupportedNetwork.NBitcoinNetwork); Assert.True(signedPSBT.TryFinalize(out _)); Assert.True(signedPSBT2.TryFinalize(out _)); Assert.Equal(signedPSBT, signedPSBT2); var ready = (await walletController.WalletPSBT(walletId, new WalletPSBTViewModel { SigningContext = new SigningContextModel(signedPSBT) })).AssertViewModel <WalletPSBTViewModel>(); Assert.Equal(signedPSBT.ToBase64(), ready.SigningContext.PSBT); psbt = AssertRedirectedPSBT(await walletController.WalletPSBTReady(walletId, ready, command: "analyze-psbt"), nameof(walletController.WalletPSBT)); Assert.Equal(signedPSBT.ToBase64(), psbt); var redirect = Assert.IsType <RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, ready, command: "broadcast")); Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName); //test base64 psbt file Assert.False(string.IsNullOrEmpty(Assert.IsType <WalletPSBTViewModel>( Assert.IsType <ViewResult>( await walletController.WalletPSBT(walletId, new WalletPSBTViewModel { UploadedPSBTFile = TestUtils.GetFormFile("base64", signedPSBT.ToBase64()) })).Model).PSBT)); }
public async Task PaymentControllerTests() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(); var client = await user.CreateClient(Policies.Unrestricted); var viewOnly = await user.CreateClient(Policies.CanViewPaymentRequests); //create payment request //validation errors await AssertValidationError(new[] { "Amount", "Currency" }, async() => { await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A" }); }); await AssertValidationError(new[] { "Amount" }, async() => { await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "BTC", Amount = 0 }); }); await AssertValidationError(new[] { "Currency" }, async() => { await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "helloinvalid", Amount = 1 }); }); await AssertHttpError(403, async() => { await viewOnly.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "helloinvalid", Amount = 1 }); }); var newPaymentRequest = await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "USD", Amount = 1 }); //list payment request var paymentRequests = await viewOnly.GetPaymentRequests(user.StoreId); Assert.NotNull(paymentRequests); Assert.Single(paymentRequests); Assert.Equal(newPaymentRequest.Id, paymentRequests.First().Id); //get payment request var paymentRequest = await viewOnly.GetPaymentRequest(user.StoreId, newPaymentRequest.Id); Assert.Equal(newPaymentRequest.Title, paymentRequest.Title); //update payment request var updateRequest = JObject.FromObject(paymentRequest).ToObject <UpdatePaymentRequestRequest>(); updateRequest.Title = "B"; await AssertHttpError(403, async() => { await viewOnly.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest); }); await client.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest); paymentRequest = await client.GetPaymentRequest(user.StoreId, newPaymentRequest.Id); Assert.Equal(updateRequest.Title, paymentRequest.Title); //archive payment request await AssertHttpError(403, async() => { await viewOnly.ArchivePaymentRequest(user.StoreId, paymentRequest.Id); }); await client.ArchivePaymentRequest(user.StoreId, paymentRequest.Id); Assert.DoesNotContain(paymentRequest.Id, (await client.GetPaymentRequests(user.StoreId)).Select(data => data.Id)); //let's test some payment stuff await user.RegisterDerivationSchemeAsync("BTC"); var paymentTestPaymentRequest = await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Amount = 0.1m, Currency = "BTC", Title = "Payment test title" }); var invoiceId = Assert.IsType <string>(Assert.IsType <OkObjectResult>(await user.GetController <PaymentRequestController>() .PayPaymentRequest(paymentTestPaymentRequest.Id, false)).Value); var invoice = user.BitPay.GetInvoice(invoiceId); await tester.WaitForEvent <InvoiceDataChangedEvent>(async() => { await tester.ExplorerNode.SendToAddressAsync( BitcoinAddress.Create(invoice.BitcoinAddress, tester.ExplorerNode.Network), invoice.BtcDue); }); await TestUtils.EventuallyAsync(async() => { Assert.Equal(Invoice.STATUS_PAID, user.BitPay.GetInvoice(invoiceId).Status); Assert.Equal(PaymentRequestData.PaymentRequestStatus.Completed, (await client.GetPaymentRequest(user.StoreId, paymentTestPaymentRequest.Id)).Status); }); } }
public BitcoinSecret DumpPrivKey(BitcoinAddress address) { var response = SendCommand("dumpprivkey", address.ToString()); return(Network.CreateFromBase58Data <BitcoinSecret>((string)response.Result)); }
public Transaction FundRawTx(RPCClient rpc, Transaction rawTx, Money feeAmount, BitcoinAddress changeAddress) { // The transaction funding logic will ensure that a transaction fee of // feeAmount is included. The remaining difference between the value of // the inputs and the outputs will be returned as a change address // output var unspentOutputs = rpc.ListUnspent(); var totalFunded = new Money(0); foreach (var unspent in unspentOutputs) { if (!unspent.IsSpendable) { continue; } if (totalFunded < (rawTx.TotalOut + feeAmount)) { rawTx.Inputs.Add(new TxIn() { PrevOut = unspent.OutPoint }); // By this point the input array will have at least one element // starting at index 0 rawTx.Inputs[rawTx.Inputs.Count - 1].ScriptSig = unspent.ScriptPubKey; // Need to accurately account for how much funding is assigned // to the inputs so that change can be correctly calculated later totalFunded += unspent.Amount; } else { break; } } if (totalFunded < (rawTx.TotalOut + feeAmount)) { throw new Exception("Insufficient unspent funds for registration"); } var change = totalFunded - rawTx.TotalOut - feeAmount; if (change < 0) { throw new Exception("Change amount cannot be negative for registration transaction"); } rawTx.Outputs.Add(new TxOut() { Value = change, ScriptPubKey = changeAddress.ScriptPubKey }); return(rawTx); }
internal AddressHistoryRecord(BitcoinAddress address, BalanceOperation operation) { Address = address; Operation = operation; }
/// <summary> /// Requires wallet support. Requires an unlocked wallet or an unencrypted wallet. /// </summary> /// <param name="address">A P2PKH or P2SH address to which the bitcoins should be sent</param> /// <param name="amount">The amount to spend</param> /// <param name="commentTx">A locally-stored (not broadcast) comment assigned to this transaction. Default is no comment</param> /// <param name="commentDest">A locally-stored (not broadcast) comment assigned to this transaction. Meant to be used for describing who the payment was sent to. Default is no comment</param> /// <returns>The TXID of the sent transaction</returns> public uint256 SendToAddress(BitcoinAddress address, Money amount, string commentTx = null, string commentDest = null) { uint256 txid = null; try { txid = SendToAddressAsync(address, amount, commentTx, commentDest).Result; } catch(AggregateException aex) { ExceptionDispatchInfo.Capture(aex.InnerException).Throw(); } return txid; }
public void DepositCheckNewTransactionsTest_TestIfDepositHandlingIsDoneAsExpected_VerifiesThroughReturnedValue() { // Submits withdraw to own address, after the first NewTransactionInterval has been elapsed. // Checks if new deposit has been created by the DepositAPplicationService and DepositAddress marked used if (_shouldRunTests) { ICoinClientService coinClientService = (ICoinClientService)ContextRegistry.GetContext()["DogecoinClientService"]; IFundsPersistenceRepository fundsPersistenceRepository = (IFundsPersistenceRepository)ContextRegistry.GetContext()["FundsPersistenceRepository"]; IDepositAddressRepository depositAddressRepository = (IDepositAddressRepository)ContextRegistry.GetContext()["DepositAddressRepository"]; IDepositRepository depositRepository = (IDepositRepository)ContextRegistry.GetContext()["DepositRepository"]; Currency currency = new Currency("DOGE", true); AccountId accountId = new AccountId(1); string newAddress = coinClientService.CreateNewAddress(); BitcoinAddress bitcoinAddress = new BitcoinAddress(newAddress); DepositAddress depositAddress = new DepositAddress(currency, bitcoinAddress, AddressStatus.New, DateTime.Now, accountId); fundsPersistenceRepository.SaveOrUpdate(depositAddress); // Check that there is no deposit with this address present List <Deposit> deposits = depositRepository.GetDepositsByBitcoinAddress(bitcoinAddress); Assert.AreEqual(0, deposits.Count); ManualResetEvent manualResetEvent = new ManualResetEvent(false); // Wait for the first interval to elapse, and then withdraw, because only then we will be able to figure if a // new transaction has been received manualResetEvent.WaitOne(Convert.ToInt32(coinClientService.PollingInterval + 3000)); manualResetEvent.Reset(); bool eventFired = false; coinClientService.DepositArrived += delegate(string curr, List <Tuple <string, string, decimal, string> > pendingList) { eventFired = true; manualResetEvent.Set(); }; Withdraw withdraw = new Withdraw(currency, Guid.NewGuid().ToString(), DateTime.Now, WithdrawType.Bitcoin, Amount, 0.001m, TransactionStatus.Pending, accountId, new BitcoinAddress(newAddress)); string commitWithdraw = coinClientService.CommitWithdraw(withdraw.BitcoinAddress.Value, withdraw.Amount); Assert.IsNotNull(commitWithdraw); Assert.IsFalse(string.IsNullOrEmpty(commitWithdraw)); manualResetEvent.WaitOne(); Assert.IsTrue(eventFired); depositAddress = depositAddressRepository.GetDepositAddressByAddress(bitcoinAddress); Assert.IsNotNull(depositAddress); Assert.AreEqual(AddressStatus.Used, depositAddress.Status); // See If DepositApplicationService created the deposit instance deposits = depositRepository.GetDepositsByBitcoinAddress(bitcoinAddress); Deposit deposit = deposits.Single(); Assert.IsNotNull(deposit); Assert.AreEqual(Amount, deposit.Amount); Assert.AreEqual(currency.Name, deposit.Currency.Name); Assert.IsFalse(string.IsNullOrEmpty(deposit.TransactionId.Value)); Assert.AreEqual(bitcoinAddress.Value, deposit.BitcoinAddress.Value); Assert.AreEqual(DepositType.Default, deposit.Type); Assert.AreEqual(0, deposit.Confirmations); Assert.AreEqual(accountId.Value, deposit.AccountId.Value); Assert.AreEqual(TransactionStatus.Pending, deposit.Status); } }
public void ImportAddress(BitcoinAddress address) { SendCommand("importaddress", address.ToString()); }
public async Task <BitcoinAddress> NewAddressAsync() { var obj = await SendCommandAsync <JObject>("newaddr"); return(BitcoinAddress.Create(obj.Property("address").Value.Value <string>(), Network)); }
public async Task ImportAddressAsync(BitcoinAddress address) { await SendCommandAsync("importaddress", address.ToString()).ConfigureAwait(false); }
public static void WaitForTransaction(this LongPollingNotificationSession session, BitcoinAddress address, uint256 txId) { session.WaitForTransaction(TrackedSource.Create(address), txId); }
public BitcoinSecret DumpPrivKey(BitcoinAddress address) { var response = SendCommand("dumpprivkey", address.ToString()); return Network.CreateFromBase58Data<BitcoinSecret>((string)response.Result); }
private static TrackedSource GetTrackedSource(DerivationStrategyBase derivationScheme, BitcoinAddress address) { TrackedSource trackedSource = null; if (address != null) { trackedSource = new AddressTrackedSource(address); } if (derivationScheme != null) { trackedSource = new DerivationSchemeTrackedSource(derivationScheme); } return(trackedSource); }
public void CanParseColoredAddress() { var address = new BitcoinAddress("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM"); var colored = address.ToColoredAddress(); Assert.Equal("akB4NBW9UuCmHuepksob6yfZs6naHtRCPNy", colored.ToWif()); Assert.Equal(address.ScriptPubKey, colored.ScriptPubKey); var testAddress = address.ToNetwork(Network.TestNet); var testColored = testAddress.ToColoredAddress(); Assert.Equal(Network.TestNet, testAddress.Network); Assert.Equal(address.Hash, testAddress.Hash); Assert.Equal(colored.ToNetwork(Network.TestNet), testColored); Assert.Equal(testAddress.ScriptPubKey, testColored.ScriptPubKey); Assert.Equal(Network.TestNet, testColored.Network); testColored = new BitcoinColoredAddress("bWqaKUZETiECYgmJNbNZUoanBxnAzoVjCNx"); Assert.Equal(Network.TestNet, testColored.Network); Assert.Equal(colored.ToNetwork(Network.TestNet), testColored); }
public async Task <IActionResult> GetTransactions( string cryptoCode, [ModelBinder(BinderType = typeof(DerivationStrategyModelBinder))] DerivationStrategyBase derivationScheme, [ModelBinder(BinderType = typeof(BitcoinAddressModelBinder))] BitcoinAddress address, [ModelBinder(BinderType = typeof(UInt256ModelBinding))] uint256 txId = null, bool includeTransaction = true) { var trackedSource = GetTrackedSource(derivationScheme, address); if (trackedSource == null) { throw new ArgumentNullException(nameof(trackedSource)); } TransactionInformation fetchedTransactionInfo = null; var network = GetNetwork(cryptoCode, false); var chain = ChainProvider.GetChain(network); var repo = RepositoryProvider.GetRepository(network); var response = new GetTransactionsResponse(); int currentHeight = chain.Height; response.Height = currentHeight; var txs = await GetAnnotatedTransactions(repo, chain, trackedSource, txId); foreach (var item in new[] { new { TxSet = response.ConfirmedTransactions, AnnotatedTx = txs.ConfirmedTransactions }, new { TxSet = response.UnconfirmedTransactions, AnnotatedTx = txs.UnconfirmedTransactions }, new { TxSet = response.ReplacedTransactions, AnnotatedTx = txs.ReplacedTransactions }, }) { foreach (var tx in item.AnnotatedTx) { var txInfo = new TransactionInformation() { BlockHash = tx.Height.HasValue ? tx.Record.BlockHash : null, Height = tx.Height, TransactionId = tx.Record.TransactionHash, Transaction = includeTransaction ? tx.Record.Transaction : null, Confirmations = tx.Height.HasValue ? currentHeight - tx.Height.Value + 1 : 0, Timestamp = tx.Record.FirstSeen, Inputs = tx.Record.SpentOutpoints.Select(o => txs.GetUTXO(o)).Where(o => o != null).ToList(), Outputs = tx.Record.GetReceivedOutputs().ToList() }; if (txId == null || txId == txInfo.TransactionId) { item.TxSet.Transactions.Add(txInfo); } if (txId != null && txId == txInfo.TransactionId) { fetchedTransactionInfo = txInfo; } txInfo.BalanceChange = txInfo.Outputs.Select(o => o.Value).Sum() - txInfo.Inputs.Select(o => o.Value).Sum(); } item.TxSet.Transactions.Reverse(); // So the youngest transaction is generally first } if (txId == null) { return(Json(response)); } else if (fetchedTransactionInfo == null) { return(NotFound()); } else { return(Json(fetchedTransactionInfo)); } }
private Coin RandomCoin(Money amount, BitcoinAddress receiver) { var outpoint = RandOutpoint(); return new Coin(outpoint, new TxOut(amount, receiver)); }
public void CanGenerateScriptFromAddress() { var address = new BitcoinAddress(new KeyId("47376c6f537d62177a2c41c4ca9b45829ab99083"), Network.Main); Assert.Equal("OP_DUP OP_HASH160 47376c6f537d62177a2c41c4ca9b45829ab99083 OP_EQUALVERIFY OP_CHECKSIG", address.ScriptPubKey.ToString()); var scriptAddress = new BitcoinScriptAddress(new ScriptId("8f55563b9a19f321c211e9b9f38cdf686ea07845"), Network.Main); Assert.Equal("OP_HASH160 8f55563b9a19f321c211e9b9f38cdf686ea07845 OP_EQUAL", scriptAddress.ScriptPubKey.ToString()); var pubKey = new PubKey("0359d3092e4a8d5f3b3948235b5dec7395259273ccf3c4e9d5e16695a3fc9588d6"); Assert.Equal("OP_DUP OP_HASH160 4d29186f76581c7375d70499afd1d585149d42cd OP_EQUALVERIFY OP_CHECKSIG", pubKey.Hash.ScriptPubKey.ToString()); Assert.Equal("0359d3092e4a8d5f3b3948235b5dec7395259273ccf3c4e9d5e16695a3fc9588d6 OP_CHECKSIG", pubKey.ScriptPubKey.ToString()); Script script = new Script("0359d3092e4a8d5f3b3948235b5dec7395259273ccf3c4e9d5e16695a3fc9588d6 OP_CHECKSIG"); Assert.Equal("OP_HASH160 a216e3bce8c1b3adf376731b6cd0b6936c4e053f OP_EQUAL", script.PaymentScript.ToString()); }