コード例 #1
0
ファイル: SeleniumTests.cs プロジェクト: mhrubel/btcpayserver
        public async Task CanUseCoinSelection()
        {
            using (var s = SeleniumTester.Create())
            {
                await s.StartAsync();

                var userId  = s.RegisterNewUser(true);
                var storeId = s.CreateNewStore().storeId;
                s.GenerateWallet("BTC", "", false, true);
                var walletId = new WalletId(storeId, "BTC");
                s.GoToWallet(walletId, WalletsNavPages.Receive);
                s.Driver.FindElement(By.Id("generateButton")).Click();
                var addressStr = s.Driver.FindElement(By.Id("vue-address")).GetProperty("value");
                var address    = BitcoinAddress.Create(addressStr, ((BTCPayNetwork)s.Server.NetworkProvider.GetNetwork("BTC")).NBitcoinNetwork);
                await s.Server.ExplorerNode.GenerateAsync(1);

                for (int i = 0; i < 6; i++)
                {
                    await s.Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(1.0m));
                }
                var targetTx = await s.Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(1.2m));

                var tx = await s.Server.ExplorerNode.GetRawTransactionAsync(targetTx);

                var spentOutpoint = new OutPoint(targetTx, tx.Outputs.FindIndex(txout => txout.Value == Money.Coins(1.2m)));
                await TestUtils.EventuallyAsync(async() =>
                {
                    var store = await s.Server.PayTester.StoreRepository.FindStore(storeId);
                    var x     = store.GetSupportedPaymentMethods(s.Server.NetworkProvider)
                                .OfType <DerivationSchemeSettings>()
                                .Single(settings => settings.PaymentId.CryptoCode == walletId.CryptoCode);
                    Assert.Contains(
                        await s.Server.PayTester.GetService <BTCPayWalletProvider>().GetWallet(walletId.CryptoCode)
                        .GetUnspentCoins(x.AccountDerivation),
                        coin => coin.OutPoint == spentOutpoint);
                });

                await s.Server.ExplorerNode.GenerateAsync(1);

                s.GoToWallet(walletId, WalletsNavPages.Send);
                s.Driver.FindElement(By.Id("advancedSettings")).Click();
                s.Driver.FindElement(By.Id("toggleInputSelection")).Click();
                s.Driver.WaitForElement(By.Id(spentOutpoint.ToString()));
                Assert.Equal("true", s.Driver.FindElement(By.Name("InputSelection")).GetAttribute("value").ToLowerInvariant());
                var el = s.Driver.FindElement(By.Id(spentOutpoint.ToString()));
                s.Driver.FindElement(By.Id(spentOutpoint.ToString())).Click();
                var inputSelectionSelect = s.Driver.FindElement(By.Name("SelectedInputs"));
                Assert.Single(inputSelectionSelect.FindElements(By.CssSelector("[selected]")));

                var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);
                SetTransactionOutput(s, 0, bob, 0.3m);
                s.Driver.FindElement(By.Id("SendMenu")).Click();
                s.Driver.FindElement(By.Id("spendWithNBxplorer")).Click();
                s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
                var happyElement = s.AssertHappyMessage();
                var happyText    = happyElement.Text;
                var txid         = Regex.Match(happyText, @"\((.*)\)").Groups[1].Value;

                tx = await s.Server.ExplorerNode.GetRawTransactionAsync(new uint256(txid));

                Assert.Single(tx.Inputs);
                Assert.Equal(spentOutpoint, tx.Inputs[0].PrevOut);
            }
        }
コード例 #2
0
        public async Task <BitcoinAddress> NewAddressAsync()
        {
            var obj = await SendCommandAsync <JObject>("newaddr");

            return(BitcoinAddress.Create(obj.Property("address").Value.Value <string>(), Network));
        }
コード例 #3
0
        protected async Task <ChainedHeader> MineBlockAtTimestampAsync(uint timestamp)
        {
            ChainedHeader tip = this.consensusManager.Tip;

            // Timestamp should always be greater than prev one.
            if (timestamp <= tip.Header.Time)
            {
                // Can happen only when target spacing had crazy low value or key was compromised and someone is mining with our key.
                this.logger.LogWarning("Somehow another block was connected with greater timestamp. Dropping current block.");
                this.logger.LogTrace("(-)[ANOTHER_BLOCK_CONNECTED]:null");
                return(null);
            }

            // If an address is specified for mining then preferentially use that.
            // The private key for this address is not used for block signing, so it can be any valid address.
            // Since it is known which miner mines in each block already it does not change the privacy level that every block mines to the same address.
            if (!string.IsNullOrWhiteSpace(this.settings.MineAddress))
            {
                this.walletScriptPubKey = BitcoinAddress.Create(this.settings.MineAddress, this.network).ScriptPubKey;
            }
            else
            {
                // Get the first address from the wallet. In a network with an account-based model the mined UTXOs should all be sent to a predictable address.
                if (this.walletScriptPubKey == null || this.walletScriptPubKey == Script.Empty)
                {
                    this.walletScriptPubKey = this.GetScriptPubKeyFromWallet();

                    // The node could not have a wallet, or the first account/address could have been incorrectly created.
                    if (this.walletScriptPubKey == null)
                    {
                        this.logger.LogWarning("The miner wasn't able to get an address from the wallet, you will not receive any rewards (if no wallet exists, please create one).");
                        this.walletScriptPubKey = new Script();
                    }
                }
            }

            BlockTemplate blockTemplate = this.blockDefinition.Build(tip, this.walletScriptPubKey);

            this.FillBlockTemplate(blockTemplate, out bool dropTemplate);

            if (dropTemplate)
            {
                this.logger.LogTrace("(-)[DROPPED]:null");
                return(null);
            }

            blockTemplate.Block.Header.Time = timestamp;

            // Update merkle root.
            blockTemplate.Block.UpdateMerkleRoot();

            // Sign block with our private key.
            var header = blockTemplate.Block.Header as PoABlockHeader;

            this.poaHeaderValidator.Sign(this.federationManager.CurrentFederationKey, header);

            ChainedHeader chainedHeader = await this.consensusManager.BlockMinedAsync(blockTemplate.Block).ConfigureAwait(false);

            if (chainedHeader == null)
            {
                // Block wasn't accepted because we already connected block from the network.
                this.logger.LogTrace("(-)[FAILED_TO_CONNECT]:null");
                return(null);
            }

            ValidationContext result = this.integrityValidator.VerifyBlockIntegrity(chainedHeader, blockTemplate.Block);

            if (result.Error != null)
            {
                // Sanity check. Should never happen.
                this.logger.LogTrace("(-)[INTEGRITY_FAILURE]");
                throw new Exception(result.Error.ToString());
            }

            return(chainedHeader);
        }
コード例 #4
0
        private TransactionBuildContext GetSetupTransactionBuildContext(IWalletTransactionHandler walletTransactionHandler,
                                                                        string coldWalletAddress, string hotWalletAddress, string walletName, string walletAccount,
                                                                        string walletPassword, Money amount, Money feeAmount, bool subtractFeeFromAmount, bool offline, bool useSegwitChangeAddress, int splitCount, ExtPubKey extPubKey = null)
        {
            Guard.NotNull(walletTransactionHandler, nameof(walletTransactionHandler));
            Guard.NotEmpty(coldWalletAddress, nameof(coldWalletAddress));
            Guard.NotEmpty(hotWalletAddress, nameof(hotWalletAddress));
            Guard.NotEmpty(walletName, nameof(walletName));
            Guard.NotEmpty(walletAccount, nameof(walletAccount));
            Guard.NotNull(amount, nameof(amount));

            Wallet.Wallet wallet = this.GetWallet(walletName);

            KeyId hotPubKeyHash  = null;
            KeyId coldPubKeyHash = null;

            if (!offline)
            {
                // Get/create the cold staking accounts.
                HdAccount coldAccount = this.GetOrCreateColdStakingAccount(walletName, true, walletPassword, extPubKey);
                HdAccount hotAccount  = this.GetOrCreateColdStakingAccount(walletName, false, walletPassword, extPubKey);

                HdAddress coldAddress = coldAccount?.ExternalAddresses.FirstOrDefault(s => s.Address == coldWalletAddress || s.Bech32Address == coldWalletAddress);
                HdAddress hotAddress  = hotAccount?.ExternalAddresses.FirstOrDefault(s => s.Address == hotWalletAddress || s.Bech32Address == hotWalletAddress);

                bool thisIsColdWallet = coldAddress != null;
                bool thisIsHotWallet  = hotAddress != null;

                this.logger.LogDebug("Local wallet '{0}' does{1} contain cold wallet address '{2}' and does{3} contain hot wallet address '{4}'.",
                                     walletName, thisIsColdWallet ? "" : " NOT", coldWalletAddress, thisIsHotWallet ? "" : " NOT", hotWalletAddress);

                if (thisIsColdWallet && thisIsHotWallet)
                {
                    this.logger.LogTrace("(-)[COLDSTAKE_BOTH_HOT_AND_COLD]");
                    throw new WalletException("You can't use this wallet as both the hot wallet and cold wallet.");
                }

                if (!thisIsColdWallet && !thisIsHotWallet)
                {
                    this.logger.LogTrace("(-)[COLDSTAKE_ADDRESSES_NOT_IN_ACCOUNTS]");
                    throw new WalletException("The hot and cold wallet addresses could not be found in the corresponding accounts.");
                }

                // Check if this is a segwit address.
                if (coldAddress?.Bech32Address == coldWalletAddress || hotAddress?.Bech32Address == hotWalletAddress)
                {
                    hotPubKeyHash  = new BitcoinWitPubKeyAddress(hotWalletAddress, wallet.Network).Hash.AsKeyId();
                    coldPubKeyHash = new BitcoinWitPubKeyAddress(coldWalletAddress, wallet.Network).Hash.AsKeyId();
                }
                else
                {
                    hotPubKeyHash  = new BitcoinPubKeyAddress(hotWalletAddress, wallet.Network).Hash;
                    coldPubKeyHash = new BitcoinPubKeyAddress(coldWalletAddress, wallet.Network).Hash;
                }
            }
            else
            {
                // In offline mode we relax all the restrictions to enable simpler setup. The user should ensure they are using separate wallets, or the cold private key could be inadvertently loaded on the online node.
                IDestination hot  = BitcoinAddress.Create(hotWalletAddress, this.network);
                IDestination cold = BitcoinAddress.Create(coldWalletAddress, this.network);

                if (hot is BitcoinPubKeyAddress && cold is BitcoinPubKeyAddress)
                {
                    hotPubKeyHash  = new BitcoinPubKeyAddress(hotWalletAddress, wallet.Network).Hash;
                    coldPubKeyHash = new BitcoinPubKeyAddress(coldWalletAddress, wallet.Network).Hash;
                }

                if (hot is BitcoinWitPubKeyAddress && cold is BitcoinWitPubKeyAddress)
                {
                    hotPubKeyHash  = new BitcoinWitPubKeyAddress(hotWalletAddress, wallet.Network).Hash.AsKeyId();
                    coldPubKeyHash = new BitcoinWitPubKeyAddress(coldWalletAddress, wallet.Network).Hash.AsKeyId();
                }
            }

            if (hotPubKeyHash == null || coldPubKeyHash == null)
            {
                this.logger.LogTrace("(-)[PUBKEYHASH_NOT_AVAILABLE]");
                throw new WalletException($"Unable to compute the needed hashes from the given addresses.");
            }

            Script destination = ColdStakingScriptTemplate.Instance.GenerateScriptPubKey(hotPubKeyHash, coldPubKeyHash);

            // Only normal accounts should be allowed.
            if (!this.GetAccounts(walletName).Any(a => a.Name == walletAccount))
            {
                this.logger.LogTrace("(-)[COLDSTAKE_ACCOUNT_NOT_FOUND]");
                throw new WalletException($"Can't find wallet account '{walletAccount}'.");
            }

            List <Recipient> recipients = GetRecipients(destination, amount, subtractFeeFromAmount, splitCount);

            var context = new TransactionBuildContext(wallet.Network)
            {
                AccountReference       = new WalletAccountReference(walletName, walletAccount),
                TransactionFee         = feeAmount,
                MinConfirmations       = 0,
                Shuffle                = false,
                UseSegwitChangeAddress = useSegwitChangeAddress,
                WalletPassword         = walletPassword,
                Recipients             = recipients
            };

            // Register the cold staking builder extension with the transaction builder.
            context.TransactionBuilder.Extensions.Add(new ColdStakingBuilderExtension(false));

            return(context);
        }
コード例 #5
0
        public async Task CanManageWallet()
        {
            using (var s = SeleniumTester.Create())
            {
                await s.StartAsync();

                s.RegisterNewUser(true);
                s.CreateNewStore();

                // In this test, we try to spend from a manual seed. We import the xpub 49'/0'/0', then try to use the seed
                // to sign the transaction
                var mnemonic = "usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage";
                var root     = new Mnemonic(mnemonic).DeriveExtKey();
                s.AddDerivationScheme("BTC", "ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD");
                var tx = s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create("bcrt1qmxg8fgnmkp354vhe78j6sr4ut64tyz2xyejel4", Network.RegTest), Money.Coins(3.0m));
                s.Server.ExplorerNode.Generate(1);

                s.Driver.FindElement(By.Id("Wallets")).Click();
                s.Driver.FindElement(By.LinkText("Manage")).Click();

                s.ClickOnAllSideMenus();

                // Make sure we can rescan, because we are admin!
                s.Driver.FindElement(By.Id("WalletRescan")).ForceClick();
                Assert.Contains("The batch size make sure", s.Driver.PageSource);

                // We setup the fingerprint and the account key path
                s.Driver.FindElement(By.Id("WalletSettings")).ForceClick();
                s.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).SendKeys("8bafd160");
                s.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).SendKeys("m/49'/0'/0'" + Keys.Enter);

                // Check the tx sent earlier arrived
                s.Driver.FindElement(By.Id("WalletTransactions")).ForceClick();
                var walletTransactionLink = s.Driver.Url;
                Assert.Contains(tx.ToString(), s.Driver.PageSource);


                void SignWith(string signingSource)
                {
                    // Send to bob
                    s.Driver.FindElement(By.Id("WalletSend")).Click();
                    var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);

                    SetTransactionOutput(0, bob, 1);
                    s.Driver.ScrollTo(By.Id("SendMenu"));
                    s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
                    s.Driver.FindElement(By.CssSelector("button[value=seed]")).Click();

                    // Input the seed
                    s.Driver.FindElement(By.Id("SeedOrKey")).SendKeys(signingSource + Keys.Enter);

                    // Broadcast
                    Assert.Contains(bob.ToString(), s.Driver.PageSource);
                    Assert.Contains("1.00000000", s.Driver.PageSource);
                    s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
                    Assert.Equal(walletTransactionLink, s.Driver.Url);
                }

                void SetTransactionOutput(int index, BitcoinAddress dest, decimal amount, bool subtract = false)
                {
                    s.Driver.FindElement(By.Id($"Outputs_{index}__DestinationAddress")).SendKeys(dest.ToString());
                    var amountElement = s.Driver.FindElement(By.Id($"Outputs_{index}__Amount"));

                    amountElement.Clear();
                    amountElement.SendKeys(amount.ToString());
                    var checkboxElement = s.Driver.FindElement(By.Id($"Outputs_{index}__SubtractFeesFromOutput"));

                    if (checkboxElement.Selected != subtract)
                    {
                        checkboxElement.Click();
                    }
                }

                SignWith(mnemonic);
                var accountKey = root.Derive(new KeyPath("m/49'/0'/0'")).GetWif(Network.RegTest).ToString();
                SignWith(accountKey);
            }
        }
コード例 #6
0
        public async Task CanPayWithTwoCurrencies()
        {
            using (var tester = CreateServerTester())
            {
                tester.ActivateLTC();
                await tester.StartAsync();

                var user = tester.NewAccount();
                await user.GrantAccessAsync();

                user.RegisterDerivationScheme("BTC");
                // First we try payment with a merchant having only BTC
                var invoice = await user.BitPay.CreateInvoiceAsync(
                    new Invoice
                {
                    Price             = 5000.0m,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true
                }, Facade.Merchant);

                var cashCow = tester.ExplorerNode;
                await cashCow.GenerateAsync(2); // get some money in case

                var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
                var firstPayment   = Money.Coins(0.04m);
                await cashCow.SendToAddressAsync(invoiceAddress, firstPayment);

                TestUtils.Eventually(() =>
                {
                    invoice = user.BitPay.GetInvoice(invoice.Id);
                    Assert.True(invoice.BtcPaid == firstPayment);
                });

                Assert.Single(invoice.CryptoInfo); // Only BTC should be presented

                var controller = tester.PayTester.GetController <UIInvoiceController>(null);
                var checkout   =
                    (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id, null)
                                                          .GetAwaiter().GetResult()).Value;
                Assert.Single(checkout.AvailableCryptos);
                Assert.Equal("BTC", checkout.CryptoCode);

                Assert.Single(invoice.PaymentCodes);
                Assert.Single(invoice.SupportedTransactionCurrencies);
                Assert.Single(invoice.SupportedTransactionCurrencies);
                Assert.Single(invoice.PaymentSubtotals);
                Assert.Single(invoice.PaymentTotals);
                Assert.True(invoice.PaymentCodes.ContainsKey("BTC"));
                Assert.True(invoice.SupportedTransactionCurrencies.ContainsKey("BTC"));
                Assert.True(invoice.SupportedTransactionCurrencies["BTC"].Enabled);
                Assert.True(invoice.PaymentSubtotals.ContainsKey("BTC"));
                Assert.True(invoice.PaymentTotals.ContainsKey("BTC"));
                //////////////////////

                // Retry now with LTC enabled
                user.RegisterDerivationScheme("LTC");
                invoice = await user.BitPay.CreateInvoiceAsync(
                    new Invoice
                {
                    Price             = 5000.0m,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true
                }, Facade.Merchant);

                cashCow        = tester.ExplorerNode;
                invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network);
                firstPayment   = Money.Coins(0.04m);
                await cashCow.SendToAddressAsync(invoiceAddress, firstPayment);

                TestLogs.LogInformation("First payment sent to " + invoiceAddress);
                TestUtils.Eventually(() =>
                {
                    invoice = user.BitPay.GetInvoice(invoice.Id);
                    Assert.True(invoice.BtcPaid == firstPayment);
                });

                cashCow = tester.LTCExplorerNode;
                var ltcCryptoInfo = invoice.CryptoInfo.FirstOrDefault(c => c.CryptoCode == "LTC");
                Assert.NotNull(ltcCryptoInfo);
                invoiceAddress = BitcoinAddress.Create(ltcCryptoInfo.Address, cashCow.Network);
                var secondPayment = Money.Coins(decimal.Parse(ltcCryptoInfo.Due, CultureInfo.InvariantCulture));
                await cashCow.GenerateAsync(4); // LTC is not worth a lot, so just to make sure we have money...

                await cashCow.SendToAddressAsync(invoiceAddress, secondPayment);

                TestLogs.LogInformation("Second payment sent to " + invoiceAddress);
                TestUtils.Eventually(() =>
                {
                    invoice = user.BitPay.GetInvoice(invoice.Id);
                    Assert.Equal(Money.Zero, invoice.BtcDue);
                    var ltcPaid = invoice.CryptoInfo.First(c => c.CryptoCode == "LTC");
                    Assert.Equal(Money.Zero, ltcPaid.Due);
                    Assert.Equal(secondPayment, ltcPaid.CryptoPaid);
                    Assert.Equal("paid", invoice.Status);
                    Assert.False((bool)((JValue)invoice.ExceptionStatus).Value);
                });

                controller = tester.PayTester.GetController <UIInvoiceController>(null);
                checkout   = (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id, "LTC")
                                                                   .GetAwaiter().GetResult()).Value;
                Assert.Equal(2, checkout.AvailableCryptos.Count);
                Assert.Equal("LTC", checkout.CryptoCode);

                Assert.Equal(2, invoice.PaymentCodes.Count());
                Assert.Equal(2, invoice.SupportedTransactionCurrencies.Count());
                Assert.Equal(2, invoice.SupportedTransactionCurrencies.Count());
                Assert.Equal(2, invoice.PaymentSubtotals.Count());
                Assert.Equal(2, invoice.PaymentTotals.Count());
                Assert.True(invoice.PaymentCodes.ContainsKey("LTC"));
                Assert.True(invoice.SupportedTransactionCurrencies.ContainsKey("LTC"));
                Assert.True(invoice.SupportedTransactionCurrencies["LTC"].Enabled);
                Assert.True(invoice.PaymentSubtotals.ContainsKey("LTC"));
                Assert.True(invoice.PaymentTotals.ContainsKey("LTC"));

                // Check if we can disable LTC
                invoice = await user.BitPay.CreateInvoiceAsync(
                    new Invoice
                {
                    Price             = 5000.0m,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true,
                    SupportedTransactionCurrencies = new Dictionary <string, InvoiceSupportedTransactionCurrency>()
                    {
                        { "BTC", new InvoiceSupportedTransactionCurrency()
                          {
                              Enabled = true
                          } }
                    }
                }, Facade.Merchant);

                Assert.Single(invoice.CryptoInfo.Where(c => c.CryptoCode == "BTC"));
                Assert.Empty(invoice.CryptoInfo.Where(c => c.CryptoCode == "LTC"));
            }
        }
コード例 #7
0
        public async Task <IActionResult> FundTransactionAsync(string account, [FromBody] FundTransactionRequest request)
        {
            try
            {
                if (request == null || request.Password == null || request.Address == null || request.Inputs == null || request.Inputs.Count() == 0 || request.FeeType == null)
                {
                    return(new ObjectResult(new FailureResponse {
                        Message = "Bad request", Details = ""
                    }));
                }

                var fail = Global.WalletWrapper.GetAccount(account, out SafeAccount safeAccount);
                if (fail != null)
                {
                    return(new ObjectResult(fail));
                }

                BitcoinAddress address;
                try
                {
                    address = BitcoinAddress.Create(request.Address, Global.WalletWrapper.WalletJob.CurrentNetwork);
                }
                catch (Exception)
                {
                    return(new ObjectResult(new FailureResponse {
                        Message = "Wrong address", Details = ""
                    }));
                }

                var inputs = new List <OutPoint>();
                try
                {
                    foreach (var input in request.Inputs)
                    {
                        var inputParts = input.Split(":");
                        inputs.Add(new OutPoint(new uint256(inputParts[1]), int.Parse(inputParts[0])));
                    }
                }
                catch (Exception)
                {
                    return(new ObjectResult(new FailureResponse {
                        Message = "Wrong amount", Details = ""
                    }));
                }


                FeeType feeType;
                if (request.FeeType.Equals("high", StringComparison.OrdinalIgnoreCase))
                {
                    feeType = FeeType.High;
                }
                else if (request.FeeType.Equals("medium", StringComparison.OrdinalIgnoreCase))
                {
                    feeType = FeeType.Medium;
                }
                else if (request.FeeType.Equals("low", StringComparison.OrdinalIgnoreCase))
                {
                    feeType = FeeType.Low;
                }
                else
                {
                    return(new ObjectResult(new FailureResponse {
                        Message = "Wrong fee type", Details = ""
                    }));
                }

                return(new ObjectResult(await Global.WalletWrapper.BuildTransactionAsync(request.Password, safeAccount, address, Money.Zero, feeType, true, null, inputs)));
            }
            catch (Exception ex)
            {
                return(new ObjectResult(new FailureResponse {
                    Message = ex.Message, Details = ex.ToString()
                }));
            }
        }
コード例 #8
0
 public BitcoinAddress GetNewAddress()
 {
     return(BitcoinAddress.Create(SendCommand(RPCOperations.getnewaddress).Result.ToString(), Network));
 }
コード例 #9
0
        public async Task <BitcoinAddress> GetRawChangeAddressAsync()
        {
            var result = await SendCommandAsync(RPCOperations.getrawchangeaddress).ConfigureAwait(false);

            return(BitcoinAddress.Create(result.Result.ToString(), Network));
        }
コード例 #10
0
        static void Main(string[] args)
        {
            // 実際はAPIでJSONを受け取る
            string jsonString = @"{""commandType"":""issuance"",""fromTxHash"":""ce56e1d60efe0f5a3d93b837ce208f559214a5ec10cb9715ac0357475ae72576"",""fromOutputIndex"":""1"",""amount"":""100000000"",""scriptPubkey"":""76a9146255517104577282389fdce86d4e9e67f796759e88ac"",""bitcoinAddress"":""mpUtirtqBzQXuH9MRw3u1YgMFhBqRhknqu"",""bitcoinSecret"":""cTxQtwjYch3uscAPDUyUd2ZkcuMDTY3dp7X2HEvXoVzsFZiLFKYX"",""quantity"":""100000000""}";

            // string jsonString = @"{""commandType"":""send"",""fromTxHash"":""5dd0250910238a134c19a6ae5867cb239754b4d69d1a0f5589b29afba55b8315"",""fromOutputIndex"":""0"",""amount"":""2730"",""scriptPubkey"":""76a9146255517104577282389fdce86d4e9e67f796759e88ac"",""issuranceAddress"":""mpUtirtqBzQXuH9MRw3u1YgMFhBqRhknqu"",""balanceQuantity"":""100000000"",""bitcoinAddress"":""myMmSWRcRvrKPiQioF6QLfhkNkn1Krsz4J"",""bitcoinSecret"":""cTxQtwjYch3uscAPDUyUd2ZkcuMDTY3dp7X2HEvXoVzsFZiLFKYX"",""feeFromTxHash"":""5dd0250910238a134c19a6ae5867cb239754b4d69d1a0f5589b29afba55b8315"",""feeFromOutputIndex"":""1"",""feeAmount"":""99987270"",""feeScriptPubkey"":""76a9146255517104577282389fdce86d4e9e67f796759e88ac"",""quantity"":""1""}";
            // string jsonString = @"{""commandType"":""other""}";

            JsonHandler.PersonData pd = (JsonHandler.PersonData)JsonHandler.getObjectFromJson(
                jsonString,
                typeof(JsonHandler.PersonData)
                );

            Console.WriteLine(jsonString);

            if (pd.commandType.Equals("issuance"))
            {
                var coin = new Coin(
                    fromTxHash: new uint256(pd.fromTxHash),
                    fromOutputIndex: pd.fromOutputIndex,
                    amount: Money.Satoshis(pd.amount),
                    scriptPubKey: new Script(Encoders.Hex.DecodeData(pd.scriptPubkey)));

                var issuance = new IssuanceCoin(coin);

                var receiveAddress         = BitcoinAddress.Create(pd.bitcoinAddress);
                var bookKey                = new BitcoinSecret(pd.bitcoinSecret);
                TransactionBuilder builder = new TransactionBuilder();

                var tx = builder
                         .AddKeys(bookKey)
                         .AddCoins(issuance)
                         .IssueAsset(receiveAddress, new AssetMoney(issuance.AssetId, quantity: pd.quantity))
                         .SendFees(Money.Coins(0.0001m))
                         .SetChange(bookKey.GetAddress())
                         .BuildTransaction(true);

                System.Diagnostics.Debug.WriteLine(tx);
                Console.WriteLine(tx);

                System.Diagnostics.Debug.WriteLine(builder.Verify(tx));
                Console.WriteLine(builder.Verify(tx));

                System.Diagnostics.Debug.WriteLine(issuance.AssetId);
                Console.WriteLine(issuance.AssetId);

                var client = new QBitNinjaClient(Network.TestNet);
                BroadcastResponse broadcastResponse = client.Broadcast(tx).Result;

                if (!broadcastResponse.Success)
                {
                    Console.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
                    Console.WriteLine("Error message: " + broadcastResponse.Error.Reason);
                }
                else
                {
                    Console.WriteLine("Success!");
                }

                /* 連結ではbitcoindでのブロードキャストを行う
                 * using (var node = Node.ConnectToLocal(Network.TestNet)) //Connect to the node
                 * {
                 *  node.VersionHandshake(); //Say hello
                 *                           //Advertize your transaction (send just the hash)
                 *  node.SendMessage(new InvPayload(InventoryType.MSG_TX, tx.GetHash()));
                 *  //Send it
                 *  node.SendMessage(new TxPayload(tx));
                 *  Thread.Sleep(500); //Wait a bit
                 * }
                 */
            }
            else if (pd.commandType.Equals("send"))
            {
                var booka = BitcoinAddress.Create(pd.issuranceAddress);
                // System.Diagnostics.Debug.WriteLine(booka.ToColoredAddress());
                // Console.WriteLine(booka.ToColoredAddress());
                var assetId = new AssetId(booka).GetWif(Network.TestNet);
                System.Diagnostics.Debug.WriteLine(assetId);
                Console.WriteLine(assetId);

                var coin = new Coin(
                    fromTxHash: new uint256(pd.fromTxHash),
                    fromOutputIndex: pd.fromOutputIndex,
                    amount: Money.Satoshis(2730),
                    scriptPubKey: new Script(Encoders.Hex.DecodeData(pd.scriptPubkey)));
                // BitcoinAssetId assetId = new BitcoinAssetId(assetId);
                ColoredCoin colored = coin.ToColoredCoin(assetId, pd.balanceQuantity);

                var book        = BitcoinAddress.Create(pd.bitcoinAddress);
                var sendSecret  = new BitcoinSecret(pd.bitcoinSecret);
                var sendAddress = sendSecret.GetAddress();

                var forFees = new Coin(
                    fromTxHash: new uint256(pd.feeFromTxHash),
                    fromOutputIndex: pd.feeFromOutputIndex,
                    amount: Money.Satoshis(pd.feeAmount),
                    scriptPubKey: new Script(Encoders.Hex.DecodeData(pd.feeScriptPubkey)));

                TransactionBuilder builder = new TransactionBuilder();
                var tx = builder
                         .AddKeys(sendSecret)
                         .AddCoins(colored, forFees)
                         .SendAsset(book, new AssetMoney(assetId, pd.quantity))
                         .SetChange(sendAddress)
                         .SendFees(Money.Coins(0.0001m))
                         .BuildTransaction(true);
                System.Diagnostics.Debug.WriteLine(tx);
                Console.WriteLine(tx);
                System.Diagnostics.Debug.WriteLine(builder.Verify(tx));
                Console.WriteLine(builder.Verify(tx));

                var client = new QBitNinjaClient(Network.TestNet);
                BroadcastResponse broadcastResponse = client.Broadcast(tx).Result;

                if (!broadcastResponse.Success)
                {
                    Console.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
                    Console.WriteLine("Error message: " + broadcastResponse.Error.Reason);
                }
                else
                {
                    Console.WriteLine("Success!");
                }

                /* 連結ではbitcoindでのブロードキャストを行う
                 * using (var node = Node.ConnectToLocal(Network.TestNet)) //Connect to the node
                 * {
                 *  node.VersionHandshake(); //Say hello
                 *                           //Advertize your transaction (send just the hash)
                 *  node.SendMessage(new InvPayload(InventoryType.MSG_TX, tx.GetHash()));
                 *  //Send it
                 *  node.SendMessage(new TxPayload(tx));
                 *  Thread.Sleep(500); //Wait a bit
                 * }
                 */
            }
            else
            {
                Console.WriteLine("CommandTypeError");
            }

            Console.ReadKey();
        }
コード例 #11
0
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var bitcoinAddressString = reader.Value as string;

            return(BitcoinAddress.Create(bitcoinAddressString, network));
        }
コード例 #12
0
        private (KeyId collateralPubKeyHash, KeyId rewardPubKeyHash) GetPubKeyHashes(ServiceNodeSettings serviceNodeSettings, string walletName, string accountName)
        {
            var unusedAddresses   = this.walletManager.GetUnusedAddresses(new WalletAccountReference(walletName, accountName), 2).ToList();
            var collateralAddress = serviceNodeSettings.CollateralAddress ?? unusedAddresses[0].Address;
            var rewardAddress     = serviceNodeSettings.RewardAddress ?? unusedAddresses[1].Address;

            KeyId collateralPubKeyHash = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(BitcoinAddress.Create(collateralAddress, this.network).ScriptPubKey);
            KeyId rewardPubKeyHash     = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(BitcoinAddress.Create(rewardAddress, this.network).ScriptPubKey);

            return(collateralPubKeyHash, rewardPubKeyHash);
        }
コード例 #13
0
        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));
        }
コード例 #14
0
ファイル: SeleniumTests.cs プロジェクト: mhrubel/btcpayserver
        public async Task CanManageWallet()
        {
            using (var s = SeleniumTester.Create())
            {
                await s.StartAsync();

                s.RegisterNewUser(true);
                var storeId = s.CreateNewStore();

                // In this test, we try to spend from a manual seed. We import the xpub 49'/0'/0', then try to use the seed
                // to sign the transaction
                s.GenerateWallet("BTC", "", true, false);

                //let's test quickly the receive wallet page
                s.Driver.FindElement(By.Id("Wallets")).Click();
                s.Driver.FindElement(By.LinkText("Manage")).Click();

                s.Driver.FindElement(By.Id("WalletSend")).Click();
                s.Driver.ScrollTo(By.Id("SendMenu"));
                s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
                //you cant use the Sign with NBX option without saving private keys when generating the wallet.
                Assert.DoesNotContain("nbx-seed", s.Driver.PageSource);

                s.Driver.FindElement(By.Id("WalletReceive")).Click();
                //generate a receiving address
                s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
                Assert.True(s.Driver.FindElement(By.ClassName("qr-container")).Displayed);
                var receiveAddr = s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value");
                //unreserve
                s.Driver.FindElement(By.CssSelector("button[value=unreserve-current-address]")).Click();
                //generate it again, should be the same one as before as nothign got used in the meantime
                s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
                Assert.True(s.Driver.FindElement(By.ClassName("qr-container")).Displayed);
                Assert.Equal(receiveAddr, s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value"));

                //send money to addr and ensure it changed
                var sess = await s.Server.ExplorerClient.CreateWebsocketNotificationSessionAsync();

                sess.ListenAllTrackedSource();
                var nextEvent = sess.NextEventAsync();
                s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create(receiveAddr, Network.RegTest),
                                                    Money.Parse("0.1"));
                await nextEvent;
                await Task.Delay(200);

                s.Driver.Navigate().Refresh();
                s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
                Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value"));
                receiveAddr = s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value");

                //change the wallet and ensure old address is not there and generating a new one does not result in the prev one
                s.GoToStore(storeId.storeId);
                s.GenerateWallet("BTC", "", true, false);
                s.Driver.FindElement(By.Id("Wallets")).Click();
                s.Driver.FindElement(By.LinkText("Manage")).Click();
                s.Driver.FindElement(By.Id("WalletReceive")).Click();
                s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
                Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value"));

                var invoiceId = s.CreateInvoice(storeId.storeName);
                var invoice   = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId);

                var address = invoice.EntityToDTO().Addresses["BTC"];

                //wallet should have been imported to bitcoin core wallet in watch only mode.
                var result = await s.Server.ExplorerNode.GetAddressInfoAsync(BitcoinAddress.Create(address, Network.RegTest));

                Assert.True(result.IsWatchOnly);
                s.GoToStore(storeId.storeId);
                var mnemonic = s.GenerateWallet("BTC", "", true, true);

                //lets import and save private keys
                var root = mnemonic.DeriveExtKey();
                invoiceId = s.CreateInvoice(storeId.storeName);
                invoice   = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId);

                address = invoice.EntityToDTO().Addresses["BTC"];
                result  = await s.Server.ExplorerNode.GetAddressInfoAsync(BitcoinAddress.Create(address, Network.RegTest));

                //spendable from bitcoin core wallet!
                Assert.False(result.IsWatchOnly);
                var tx = s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create(address, Network.RegTest), Money.Coins(3.0m));
                s.Server.ExplorerNode.Generate(1);

                s.Driver.FindElement(By.Id("Wallets")).Click();
                s.Driver.FindElement(By.LinkText("Manage")).Click();

                s.ClickOnAllSideMenus();

                // Make sure we can rescan, because we are admin!
                s.Driver.FindElement(By.Id("WalletRescan")).ForceClick();
                Assert.Contains("The batch size make sure", s.Driver.PageSource);

                // We setup the fingerprint and the account key path
                s.Driver.FindElement(By.Id("WalletSettings")).ForceClick();
                //                s.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).SendKeys("8bafd160");
                //                s.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).SendKeys("m/49'/0'/0'" + Keys.Enter);

                // Check the tx sent earlier arrived
                s.Driver.FindElement(By.Id("WalletTransactions")).ForceClick();
                var walletTransactionLink = s.Driver.Url;
                Assert.Contains(tx.ToString(), s.Driver.PageSource);


                void SignWith(Mnemonic signingSource)
                {
                    // Send to bob
                    s.Driver.FindElement(By.Id("WalletSend")).Click();
                    var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);

                    SetTransactionOutput(s, 0, bob, 1);
                    s.Driver.ScrollTo(By.Id("SendMenu"));
                    s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
                    s.Driver.FindElement(By.CssSelector("button[value=seed]")).Click();

                    // Input the seed
                    s.Driver.FindElement(By.Id("SeedOrKey")).SendKeys(signingSource.ToString() + Keys.Enter);

                    // Broadcast
                    Assert.Contains(bob.ToString(), s.Driver.PageSource);
                    Assert.Contains("1.00000000", s.Driver.PageSource);
                    s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
                    Assert.Equal(walletTransactionLink, s.Driver.Url);
                }

                SignWith(mnemonic);

                s.Driver.FindElement(By.Id("Wallets")).Click();
                s.Driver.FindElement(By.LinkText("Manage")).Click();
                s.Driver.FindElement(By.Id("WalletSend")).Click();

                var jack = new Key().PubKey.Hash.GetAddress(Network.RegTest);
                SetTransactionOutput(s, 0, jack, 0.01m);
                s.Driver.ScrollTo(By.Id("SendMenu"));
                s.Driver.FindElement(By.Id("SendMenu")).ForceClick();

                s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
                Assert.Contains(jack.ToString(), s.Driver.PageSource);
                Assert.Contains("0.01000000", s.Driver.PageSource);
                s.Driver.FindElement(By.CssSelector("button[value=analyze-psbt]")).ForceClick();
                Assert.EndsWith("psbt", s.Driver.Url);
                s.Driver.FindElement(By.CssSelector("#OtherActions")).ForceClick();
                s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
                Assert.EndsWith("psbt/ready", s.Driver.Url);
                s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
                Assert.Equal(walletTransactionLink, s.Driver.Url);

                var bip21 = invoice.EntityToDTO().CryptoInfo.First().PaymentUrls.BIP21;
                //let's make bip21 more interesting
                bip21 += "&label=Solid Snake&message=Snake? Snake? SNAAAAKE!";
                var parsedBip21 = new BitcoinUrlBuilder(bip21, Network.RegTest);
                s.Driver.FindElement(By.Id("Wallets")).Click();
                s.Driver.FindElement(By.LinkText("Manage")).Click();
                s.Driver.FindElement(By.Id("WalletSend")).Click();
                s.Driver.FindElement(By.Id("bip21parse")).Click();
                s.Driver.SwitchTo().Alert().SendKeys(bip21);
                s.Driver.SwitchTo().Alert().Accept();
                s.AssertHappyMessage(StatusMessageModel.StatusSeverity.Info);
                Assert.Equal(parsedBip21.Amount.ToString(false), s.Driver.FindElement(By.Id($"Outputs_0__Amount")).GetAttribute("value"));
                Assert.Equal(parsedBip21.Address.ToString(), s.Driver.FindElement(By.Id($"Outputs_0__DestinationAddress")).GetAttribute("value"));

                s.GoToWallet(new WalletId(storeId.storeId, "BTC"), WalletsNavPages.Settings);
                var walletUrl = s.Driver.Url;

                s.Driver.FindElement(By.Id("SettingsMenu")).ForceClick();
                s.Driver.FindElement(By.CssSelector("button[value=view-seed]")).Click();

                // Seed backup page
                var recoveryPhrase = s.Driver.FindElements(By.Id("recovery-phrase")).First().GetAttribute("data-mnemonic");
                Assert.Equal(mnemonic.ToString(), recoveryPhrase);
                Assert.Contains("The recovery phrase will also be stored on the server as a hot wallet.", s.Driver.PageSource);

                // No confirmation, just a link to return to the wallet
                Assert.Empty(s.Driver.FindElements(By.Id("confirm")));
                s.Driver.FindElement(By.Id("proceed")).Click();
                Assert.Equal(walletUrl, s.Driver.Url);
            }
        }
コード例 #15
0
        public async Task CanSetupWallet()
        {
            using (var tester = CreateServerTester())
            {
                tester.ActivateLTC();
                tester.ActivateLightning();
                await tester.StartAsync();

                var user       = tester.NewAccount();
                var cryptoCode = "BTC";
                await user.GrantAccessAsync(true);

                user.RegisterDerivationScheme(cryptoCode);
                user.RegisterDerivationScheme("LTC");
                user.RegisterLightningNode(cryptoCode, LightningConnectionType.CLightning);
                var btcNetwork = tester.PayTester.Networks.GetNetwork <BTCPayNetwork>(cryptoCode);
                var invoice    = await user.BitPay.CreateInvoiceAsync(
                    new Invoice
                {
                    Price             = 1.5m,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true
                }, Facade.Merchant);

                Assert.Equal(3, invoice.CryptoInfo.Length);

                // Setup Lightning
                var controller  = user.GetController <UIStoresController>();
                var lightningVm = (LightningNodeViewModel)Assert.IsType <ViewResult>(controller.SetupLightningNode(user.StoreId, cryptoCode)).Model;
                Assert.True(lightningVm.Enabled);
                var response = await controller.SetLightningNodeEnabled(user.StoreId, cryptoCode, false);

                Assert.IsType <RedirectToActionResult>(response);

                // Get enabled state from settings
                LightningSettingsViewModel lnSettingsModel;
                response        = controller.LightningSettings(user.StoreId, cryptoCode);
                lnSettingsModel = (LightningSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.NotNull(lnSettingsModel?.ConnectionString);
                Assert.False(lnSettingsModel.Enabled);

                // Setup wallet
                WalletSetupViewModel setupVm;
                var storeId = user.StoreId;
                response = await controller.GenerateWallet(storeId, cryptoCode, WalletSetupMethod.GenerateOptions, new WalletSetupRequest());

                Assert.IsType <ViewResult>(response);

                // Get enabled state from settings
                response = controller.WalletSettings(user.StoreId, cryptoCode).GetAwaiter().GetResult();
                var onchainSettingsModel = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.NotNull(onchainSettingsModel?.DerivationScheme);
                Assert.True(onchainSettingsModel.Enabled);

                // Disable wallet
                onchainSettingsModel.Enabled = false;
                response = controller.UpdateWalletSettings(onchainSettingsModel).GetAwaiter().GetResult();
                Assert.IsType <RedirectToActionResult>(response);
                response             = controller.WalletSettings(user.StoreId, cryptoCode).GetAwaiter().GetResult();
                onchainSettingsModel = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.NotNull(onchainSettingsModel?.DerivationScheme);
                Assert.False(onchainSettingsModel.Enabled);

                var oldScheme = onchainSettingsModel.DerivationScheme;

                invoice = await user.BitPay.CreateInvoiceAsync(
                    new Invoice
                {
                    Price             = 1.5m,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true
                }, Facade.Merchant);

                Assert.Single(invoice.CryptoInfo);
                Assert.Equal("LTC", invoice.CryptoInfo[0].CryptoCode);

                // Removing the derivation scheme, should redirect to store page
                response = controller.ConfirmDeleteWallet(user.StoreId, cryptoCode).GetAwaiter().GetResult();
                Assert.IsType <RedirectToActionResult>(response);

                // Setting it again should show the confirmation page
                response = await controller.UpdateWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode, DerivationScheme = oldScheme });

                setupVm = (WalletSetupViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.True(setupVm.Confirmation);

                // The following part posts a wallet update, confirms it and checks the result

                // cobo vault file
                var content = "{\"ExtPubKey\":\"xpub6CEqRFZ7yZxCFXuEWZBAdnC8bdvu9SRHevaoU2SsW9ZmKhrCShmbpGZWwaR15hdLURf8hg47g4TpPGaqEU8hw5LEJCE35AUhne67XNyFGBk\",\"MasterFingerprint\":\"7a7563b5\",\"DerivationPath\":\"M\\/84'\\/0'\\/0'\",\"CoboVaultFirmwareVersion\":\"1.2.0(BTC-Only)\"}";
                response = await controller.UpdateWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("cobovault.json", content) });

                setupVm = (WalletSetupViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.True(setupVm.Confirmation);
                response = await controller.UpdateWallet(setupVm);

                Assert.IsType <RedirectToActionResult>(response);
                response = await controller.WalletSettings(storeId, cryptoCode);

                var settingsVm = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.Equal("CoboVault", settingsVm.Source);

                // wasabi wallet file
                content  = "{\r\n  \"EncryptedSecret\": \"6PYWBQ1zsukowsnTNA57UUx791aBuJusm7E4egXUmF5WGw3tcdG3cmTL57\",\r\n  \"ChainCode\": \"waSIVbn8HaoovoQg/0t8IS1+ZCxGsJRGFT21i06nWnc=\",\r\n  \"MasterFingerprint\": \"7a7563b5\",\r\n  \"ExtPubKey\": \"xpub6CEqRFZ7yZxCFXuEWZBAdnC8bdvu9SRHevaoU2SsW9ZmKhrCShmbpGZWwaR15hdLURf8hg47g4TpPGaqEU8hw5LEJCE35AUhne67XNyFGBk\",\r\n  \"PasswordVerified\": false,\r\n  \"MinGapLimit\": 21,\r\n  \"AccountKeyPath\": \"84'/0'/0'\",\r\n  \"BlockchainState\": {\r\n    \"Network\": \"RegTest\",\r\n    \"Height\": \"0\"\r\n  },\r\n  \"HdPubKeys\": []\r\n}";
                response = await controller.UpdateWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("wasabi.json", content) });

                setupVm = (WalletSetupViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.True(setupVm.Confirmation);
                response = await controller.UpdateWallet(setupVm);

                Assert.IsType <RedirectToActionResult>(response);
                response = await controller.WalletSettings(storeId, cryptoCode);

                settingsVm = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.Equal("WasabiFile", settingsVm.Source);

                // Can we upload coldcard settings? (Should fail, we are giving a mainnet file to a testnet network)
                content  = "{\"keystore\": {\"ckcc_xpub\": \"xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw\", \"xpub\": \"ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/0'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}";
                response = await controller.UpdateWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("coldcard-ypub.json", content) });

                setupVm = (WalletSetupViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.False(setupVm.Confirmation); // Should fail, we are giving a mainnet file to a testnet network

                // And with a good file? (upub)
                content  = "{\"keystore\": {\"ckcc_xpub\": \"tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS\", \"xpub\": \"upub5DBYp1qGgsTrkzCptMGZc2x18pquLwGrBw6nS59T4NViZ4cni1mGowQzziy85K8vzkp1jVtWrSkLhqk9KDfvrGeB369wGNYf39kX8rQfiLn\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/0'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}";
                response = await controller.UpdateWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("coldcard-upub.json", content) });

                setupVm = (WalletSetupViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.True(setupVm.Confirmation);
                response = await controller.UpdateWallet(setupVm);

                Assert.IsType <RedirectToActionResult>(response);
                response = await controller.WalletSettings(storeId, cryptoCode);

                settingsVm = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.Equal("ElectrumFile", settingsVm.Source);

                // Now let's check that no data has been lost in the process
                var store      = tester.PayTester.StoreRepository.FindStore(storeId).GetAwaiter().GetResult();
                var onchainBTC = store.GetSupportedPaymentMethods(tester.PayTester.Networks)
#pragma warning disable CS0618 // Type or member is obsolete
                                 .OfType <DerivationSchemeSettings>().First(o => o.PaymentId.IsBTCOnChain);
#pragma warning restore CS0618 // Type or member is obsolete
                DerivationSchemeSettings.TryParseFromWalletFile(content, onchainBTC.Network, out var expected);
                Assert.Equal(expected.ToJson(), onchainBTC.ToJson());

                // Let's check that the root hdkey and account key path are taken into account when making a PSBT
                invoice = await user.BitPay.CreateInvoiceAsync(
                    new Invoice
                {
                    Price             = 1.5m,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true
                }, Facade.Merchant);

                tester.ExplorerNode.Generate(1);
                var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo.First(c => c.CryptoCode == cryptoCode).Address,
                                                           tester.ExplorerNode.Network);
                tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(1m));
                TestUtils.Eventually(() =>
                {
                    invoice = user.BitPay.GetInvoice(invoice.Id);
                    Assert.Equal("paid", invoice.Status);
                });
                var wallet = tester.PayTester.GetController <UIWalletsController>();
                var psbt   = wallet.CreatePSBT(btcNetwork, onchainBTC,
                                               new WalletSendModel()
                {
                    Outputs = new List <WalletSendModel.TransactionOutput>
                    {
                        new WalletSendModel.TransactionOutput
                        {
                            Amount             = 0.5m,
                            DestinationAddress = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, btcNetwork.NBitcoinNetwork)
                                                 .ToString(),
                        }
                    },
                    FeeSatoshiPerByte = 1
                }, default).GetAwaiter().GetResult();

                Assert.NotNull(psbt);

                var root = new Mnemonic(
                    "usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage")
                           .DeriveExtKey().AsHDKeyCache();
                var account = root.Derive(new KeyPath("m/49'/0'/0'"));
                Assert.All(psbt.PSBT.Inputs, input =>
                {
                    var keyPath = input.HDKeyPaths.Single();
                    Assert.False(keyPath.Value.KeyPath.IsHardened);
                    Assert.Equal(account.Derive(keyPath.Value.KeyPath).GetPublicKey(), keyPath.Key);
                    Assert.Equal(keyPath.Value.MasterFingerprint,
                                 onchainBTC.AccountKeySettings[0].AccountKey.GetPublicKey().GetHDFingerPrint());
                });
            }
        }
コード例 #16
0
        public async Task <IActionResult> CreateOnChainTransaction(string storeId, string cryptoCode,
                                                                   [FromBody] CreateOnChainTransactionRequest request)
        {
            if (IsInvalidWalletRequest(cryptoCode, out BTCPayNetwork network,
                                       out DerivationSchemeSettings derivationScheme, out IActionResult actionResult))
            {
                return(actionResult);
            }
            if (network.ReadonlyWallet)
            {
                return(this.CreateAPIError("not-available",
                                           $"{cryptoCode} sending services are not currently available"));
            }

            //This API is only meant for hot wallet usage for now. We can expand later when we allow PSBT manipulation.
            if (!(await CanUseHotWallet()).HotWallet)
            {
                return(Unauthorized());
            }

            var explorerClient = _explorerClientProvider.GetExplorerClient(cryptoCode);
            var wallet         = _btcPayWalletProvider.GetWallet(network);

            var utxos = await wallet.GetUnspentCoins(derivationScheme.AccountDerivation);

            if (request.SelectedInputs != null || !utxos.Any())
            {
                utxos = utxos.Where(coin => request.SelectedInputs?.Contains(coin.OutPoint) ?? true)
                        .ToArray();
                if (utxos.Any() is false)
                {
                    //no valid utxos selected
                    request.AddModelError(transactionRequest => transactionRequest.SelectedInputs,
                                          "There are no available utxos based on your request", this);
                }
            }

            var balanceAvailable = utxos.Sum(coin => coin.Value.GetValue(network));

            var subtractFeesOutputsCount = new List <int>();
            var subtractFees             = request.Destinations.Any(o => o.SubtractFromAmount);
            int?payjoinOutputIndex       = null;
            var sum     = 0m;
            var outputs = new List <WalletSendModel.TransactionOutput>();

            for (var index = 0; index < request.Destinations.Count; index++)
            {
                var destination = request.Destinations[index];

                if (destination.SubtractFromAmount)
                {
                    subtractFeesOutputsCount.Add(index);
                }

                BitcoinUrlBuilder bip21 = null;
                var amount = destination.Amount;
                if (amount.GetValueOrDefault(0) <= 0)
                {
                    amount = null;
                }
                var address = string.Empty;
                try
                {
                    destination.Destination = destination.Destination.Replace(network.UriScheme + ":", "bitcoin:", StringComparison.InvariantCultureIgnoreCase);
                    bip21 = new BitcoinUrlBuilder(destination.Destination, network.NBitcoinNetwork);
                    amount ??= bip21.Amount.GetValue(network);
                    address = bip21.Address.ToString();
                    if (destination.SubtractFromAmount)
                    {
                        request.AddModelError(transactionRequest => transactionRequest.Destinations[index],
                                              "You cannot use a BIP21 destination along with SubtractFromAmount", this);
                    }
                }
                catch (FormatException)
                {
                    try
                    {
                        address = BitcoinAddress.Create(destination.Destination, network.NBitcoinNetwork).ToString();
                    }
                    catch (Exception)
                    {
                        request.AddModelError(transactionRequest => transactionRequest.Destinations[index],
                                              "Destination must be a BIP21 payment link or an address", this);
                    }
                }

                if (amount is null || amount <= 0)
                {
                    request.AddModelError(transactionRequest => transactionRequest.Destinations[index],
                                          "Amount must be specified or destination must be a BIP21 payment link, and greater than 0", this);
                }
                if (request.ProceedWithPayjoin && bip21?.UnknowParameters?.ContainsKey("pj") is true)
                {
                    payjoinOutputIndex = index;
                }

                outputs.Add(new WalletSendModel.TransactionOutput()
                {
                    DestinationAddress     = address,
                    Amount                 = amount,
                    SubtractFeesFromOutput = destination.SubtractFromAmount
                });
                sum += destination.Amount ?? 0;
            }

            if (subtractFeesOutputsCount.Count > 1)
            {
                foreach (var subtractFeesOutput in subtractFeesOutputsCount)
                {
                    request.AddModelError(model => model.Destinations[subtractFeesOutput].SubtractFromAmount,
                                          "You can only subtract fees from one destination", this);
                }
            }

            if (balanceAvailable < sum)
            {
                request.AddModelError(transactionRequest => transactionRequest.Destinations,
                                      "You are attempting to send more than is available", this);
            }
            else if (balanceAvailable == sum && !subtractFees)
            {
                request.AddModelError(transactionRequest => transactionRequest.Destinations,
                                      "You are sending your entire balance, you should subtract the fees from a destination", this);
            }

            var minRelayFee = _nbXplorerDashboard.Get(network.CryptoCode).Status.BitcoinStatus?.MinRelayTxFee ??
                              new FeeRate(1.0m);

            if (request.FeeRate != null && request.FeeRate < minRelayFee)
            {
                ModelState.AddModelError(nameof(request.FeeRate),
                                         "The fee rate specified is lower than the current minimum relay fee");
            }

            if (!ModelState.IsValid)
            {
                return(this.CreateValidationError(ModelState));
            }

            CreatePSBTResponse psbt;

            try
            {
                psbt = await _walletsController.CreatePSBT(network, derivationScheme,
                                                           new WalletSendModel()
                {
                    SelectedInputs = request.SelectedInputs?.Select(point => point.ToString()),
                    Outputs        = outputs,
                    AlwaysIncludeNonWitnessUTXO = true,
                    InputSelection = request.SelectedInputs?.Any() is true,
                    AllowFeeBump   =
                        !request.RBF.HasValue ? WalletSendModel.ThreeStateBool.Maybe :
                        request.RBF.Value ? WalletSendModel.ThreeStateBool.Yes :
                        WalletSendModel.ThreeStateBool.No,
                    FeeSatoshiPerByte = request.FeeRate?.SatoshiPerByte,
                    NoChange          = request.NoChange
                },
                                                           CancellationToken.None);
            }
コード例 #17
0
        private static async Task CanCreateRefundsCore(SeleniumTester s, TestAccount user, bool multiCurrency, string rateSelection)
        {
            s.GoToHome();
            s.Server.PayTester.ChangeRate("BTC_USD", new Rating.BidAsk(5000.0m, 5100.0m));
            var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
            {
                Currency = "USD",
                Price    = 5000.0m
            });

            var info     = invoice.CryptoInfo.First(o => o.CryptoCode == "BTC");
            var totalDue = decimal.Parse(info.TotalDue, CultureInfo.InvariantCulture);
            var paid     = totalDue + 0.1m;
            await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(info.Address, Network.RegTest), Money.Coins(paid));

            await s.Server.ExplorerNode.GenerateAsync(1);

            await TestUtils.EventuallyAsync(async() =>
            {
                invoice = await user.BitPay.GetInvoiceAsync(invoice.Id);
                Assert.Equal("confirmed", invoice.Status);
            });

            // BTC crash by 50%
            s.Server.PayTester.ChangeRate("BTC_USD", new Rating.BidAsk(5000.0m / 2.0m, 5100.0m / 2.0m));
            s.GoToStore();
            s.Driver.FindElement(By.Id("BOLT11Expiration")).Clear();
            s.Driver.FindElement(By.Id("BOLT11Expiration")).SendKeys("5" + Keys.Enter);
            s.GoToInvoice(invoice.Id);
            s.Driver.FindElement(By.Id("IssueRefund")).Click();

            if (multiCurrency)
            {
                s.Driver.WaitUntilAvailable(By.Id("RefundForm"), TimeSpan.FromSeconds(1));
                s.Driver.WaitUntilAvailable(By.Id("SelectedPaymentMethod"), TimeSpan.FromSeconds(1));
                s.Driver.FindElement(By.Id("SelectedPaymentMethod")).SendKeys("BTC" + Keys.Enter);
                s.Driver.FindElement(By.Id("ok")).Click();
            }

            s.Driver.WaitUntilAvailable(By.Id("RefundForm"), TimeSpan.FromSeconds(1));
            Assert.Contains("$5,500.00", s.Driver.PageSource);    // Should propose reimburse in fiat
            Assert.Contains("1.10000000 ₿", s.Driver.PageSource); // Should propose reimburse in BTC at the rate of before
            Assert.Contains("2.20000000 ₿", s.Driver.PageSource); // Should propose reimburse in BTC at the current rate
            s.Driver.WaitForAndClick(By.Id(rateSelection));
            s.Driver.FindElement(By.Id("ok")).Click();

            s.Driver.WaitUntilAvailable(By.Id("Destination"), TimeSpan.FromSeconds(1));
            Assert.Contains("pull-payments", s.Driver.Url);
            if (rateSelection == "FiatOption")
            {
                Assert.Contains("$5,500.00", s.Driver.PageSource);
            }
            if (rateSelection == "CurrentOption")
            {
                Assert.Contains("2.20000000 ₿", s.Driver.PageSource);
            }
            if (rateSelection == "RateThenOption")
            {
                Assert.Contains("1.10000000 ₿", s.Driver.PageSource);
            }

            s.GoToInvoice(invoice.Id);
            s.Driver.FindElement(By.Id("IssueRefund")).Click();
            s.Driver.WaitUntilAvailable(By.Id("Destination"), TimeSpan.FromSeconds(1));
            Assert.Contains("pull-payments", s.Driver.Url);
            var client = await user.CreateClient();

            var ppid = s.Driver.Url.Split('/').Last();
            var pps  = await client.GetPullPayments(user.StoreId);

            var pp = Assert.Single(pps, p => p.Id == ppid);

            Assert.Equal(TimeSpan.FromDays(5.0), pp.BOLT11Expiration);
        }
コード例 #18
0
 private AltTxn MultipleTransV2(ISecret[] secrects, string ToAddress, List\\ PrevTransList, string Amount, string ChangeAddress, string ExactFeeAmount, Network net)
 {
     AltTxn resp = new AltTxn();
     try
     {
         var txBuilder = net.CreateTransactionBuilder();
         Transaction tx = Transaction.Create(net);
         List\\ m1CoinsV = new List\\();
         long totalCoins = 0;
         foreach (NoxKeys.BCUnspent trans in PrevTransList)
         {
             var amount = Money.Parse(trans.Amount);
             totalCoins = totalCoins + amount.Satoshi;
             ICoin coin = new Coin(uint256.Parse(trans.TxHash), (uint)trans.OutputN, amount, BitcoinAddress.Create(trans.Address, net).ScriptPubKey);
             m1CoinsV.Add(coin);
         }
         var m2kChange = BitcoinAddress.Create(ChangeAddress, net);
         var m2k = BitcoinAddress.Create(ToAddress, net);
         tx = Transaction.Create(net);
         txBuilder.AddCoins(m1CoinsV.ToArray());
         txBuilder.AddKeys(secrects);
         txBuilder.Send(m2k, Money.Parse(Amount));
         txBuilder.SetChange(m2kChange);
         txBuilder.SendFees(Money.Parse(ExactFeeAmount));
         tx = txBuilder.BuildTransaction(true);
         tx.Version = 2;
         tx = txBuilder.SignTransaction(tx);
         if (txBuilder.Verify(tx) == true)
         {
             resp.Fee = (totalCoins - tx.TotalOut.Satoshi) * 0.00000001M;
             resp.TxnHex = tx.ToHex();
             tx = null; txBuilder = null;
             return resp;
         }
         else
         {
             resp.IsError = true;
             resp.ErrorMessage = "Error, Not fully signed.";
             tx = null;
             txBuilder = null;
             return resp;
         }
     }
     catch (Exception ex)
     {
         resp.IsError = true;
         resp.ErrorMessage = ex.Message;
         return resp;
     }
 }
コード例 #19
0
        public async Task <IActionResult> BuildTransactionAsync(string account, [FromBody] BuildTransactionRequest request)
        {
            try
            {
                if (request == null || request.Password == null || request.Address == null || request.Amount == null || request.FeeType == null)
                {
                    return(new ObjectResult(new FailureResponse {
                        Message = "Bad request", Details = ""
                    }));
                }

                var fail = Global.WalletWrapper.GetAccount(account, out SafeAccount safeAccount);
                if (fail != null)
                {
                    return(new ObjectResult(fail));
                }

                Script customChangeScriptPubKey = null;                 // if it stays null then scriptubkey is not custom
                if (request.DonateChange)
                {
                    customChangeScriptPubKey = BitcoinAddress.Create("186n7me3QKajQZJnUsVsezVhVrSwyFCCZ", Network.Main).ScriptPubKey;
                }

                BitcoinAddress address;
                try
                {
                    address = BitcoinAddress.Create(request.Address, Global.WalletWrapper.WalletJob.CurrentNetwork);
                }
                catch (Exception)
                {
                    return(new ObjectResult(new FailureResponse {
                        Message = "Wrong address", Details = ""
                    }));
                }
                Money amount = Money.Zero; // in this case all funds are sent from the wallet
                try
                {
                    if (request.Amount != "all")
                    {
                        var tmpAmount = new Money(decimal.Parse(request.Amount.Replace(',', '.'), NumberStyles.Any, CultureInfo.InvariantCulture), MoneyUnit.BTC);

                        if (tmpAmount <= Money.Zero)
                        {
                            return(new ObjectResult(new FailureResponse {
                                Message = "Amount must be > 0 or \"all\"", Details = ""
                            }));
                        }
                        amount = tmpAmount;
                    }
                }
                catch (Exception)
                {
                    return(new ObjectResult(new FailureResponse {
                        Message = "Wrong amount", Details = ""
                    }));
                }


                FeeType feeType;
                if (request.FeeType.Equals("high", StringComparison.OrdinalIgnoreCase))
                {
                    feeType = FeeType.High;
                }
                else if (request.FeeType.Equals("medium", StringComparison.OrdinalIgnoreCase))
                {
                    feeType = FeeType.Medium;
                }
                else if (request.FeeType.Equals("low", StringComparison.OrdinalIgnoreCase))
                {
                    feeType = FeeType.Low;
                }
                else
                {
                    return(new ObjectResult(new FailureResponse {
                        Message = "Wrong fee type", Details = ""
                    }));
                }

                var ret = await Global.WalletWrapper.BuildTransactionAsync(request.Password, safeAccount, address, amount, feeType, false, customChangeScriptPubKey, null);

                if (request.DonateChange && ret is BuildTransactionResponse)
                {
                    var retB = ret as BuildTransactionResponse;
                    retB.ChangeOutputAmount = "0";
                    return(new ObjectResult(retB));
                }

                return(new ObjectResult(ret));
            }
            catch (Exception ex)
            {
                return(new ObjectResult(new FailureResponse {
                    Message = ex.Message, Details = ex.ToString()
                }));
            }
        }
コード例 #20
0
        public async Task <PubKey> JoinFederationAsync(JoinFederationRequestModel request, CancellationToken cancellationToken)
        {
            // First ensure that this collateral address isnt already present in the federation.
            if (this.federationManager.GetFederationMembers().IsCollateralAddressRegistered(request.CollateralAddress))
            {
                throw new Exception($"The provided collateral address '{request.CollateralAddress}' is already present in the federation.");
            }

            // Get the address pub key hash.
            BitcoinAddress address    = BitcoinAddress.Create(request.CollateralAddress, this.counterChainSettings.CounterChainNetwork);
            KeyId          addressKey = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(address.ScriptPubKey);

            // Get mining key.
            var keyTool  = new KeyTool(this.nodeSettings.DataFolder);
            Key minerKey = keyTool.LoadPrivateKey();

            if (minerKey == null)
            {
                throw new Exception($"The private key file ({KeyTool.KeyFileDefaultName}) has not been configured or is not present.");
            }

            var expectedCollateralAmount = CollateralFederationMember.GetCollateralAmountForPubKey(this.network, minerKey.PubKey);

            var collateralAmount = new Money(expectedCollateralAmount, MoneyUnit.BTC);

            var joinRequest = new JoinFederationRequest(minerKey.PubKey, collateralAmount, addressKey);

            // Populate the RemovalEventId.
            var collateralFederationMember = new CollateralFederationMember(minerKey.PubKey, false, joinRequest.CollateralAmount, request.CollateralAddress);

            byte[] federationMemberBytes = (this.network.Consensus.ConsensusFactory as CollateralPoAConsensusFactory).SerializeFederationMember(collateralFederationMember);
            Poll   poll = this.votingManager.GetApprovedPolls().FirstOrDefault(x => x.IsExecuted &&
                                                                               x.VotingData.Key == VoteKey.KickFederationMember && x.VotingData.Data.SequenceEqual(federationMemberBytes));

            joinRequest.RemovalEventId = (poll == null) ? Guid.Empty : new Guid(poll.PollExecutedBlockData.Hash.ToBytes().TakeLast(16).ToArray());

            // Get the signature by calling the counter-chain "signmessage" API.
            var signMessageRequest = new SignMessageRequest()
            {
                Message         = joinRequest.SignatureMessage,
                WalletName      = request.CollateralWalletName,
                Password        = request.CollateralWalletPassword,
                ExternalAddress = request.CollateralAddress
            };

            var walletClient = new WalletClient(this.httpClientFactory, $"http://{this.counterChainSettings.CounterChainApiHost}", this.counterChainSettings.CounterChainApiPort);

            try
            {
                string signature = await walletClient.SignMessageAsync(signMessageRequest, cancellationToken);

                joinRequest.AddSignature(signature);
            }
            catch (Exception err)
            {
                throw new Exception($"The call to sign the join federation request failed: '{err.Message}'.");
            }

            IWalletTransactionHandler walletTransactionHandler = this.fullNode.NodeService <IWalletTransactionHandler>();
            var encoder = new JoinFederationRequestEncoder();
            JoinFederationRequestResult result = JoinFederationRequestBuilder.BuildTransaction(walletTransactionHandler, this.network, joinRequest, encoder, request.WalletName, request.WalletAccount, request.WalletPassword);

            if (result.Transaction == null)
            {
                throw new Exception(result.Errors);
            }

            IWalletService walletService = this.fullNode.NodeService <IWalletService>();
            await walletService.SendTransaction(new SendTransactionRequest(result.Transaction.ToHex()), cancellationToken);

            return(minerKey.PubKey);
        }
コード例 #21
0
        static void Main()
        {
            var publicKeyHash  = new Key().PubKey.Hash;
            var bitcoinAddress = publicKeyHash.GetAddress(Network.Main);

            Console.WriteLine(publicKeyHash);  // 41e0d7ab8af1ba5452b824116a31357dc931cf28
            Console.WriteLine(bitcoinAddress); // 171LGoEKyVzgQstGwnTHVh3TFTgo5PsqiY

            var scriptPubKey = bitcoinAddress.ScriptPubKey;

            Console.WriteLine(scriptPubKey); // OP_DUP OP_HASH160 41e0d7ab8af1ba5452b824116a31357dc931cf28 OP_EQUALVERIFY OP_CHECKSIG
            var sameBitcoinAddress = scriptPubKey.GetDestinationAddress(Network.Main);

            Block       genesisBlock         = Network.Main.GetGenesis();
            Transaction firstTransactionEver = genesisBlock.Transactions.First();

            Console.WriteLine(firstTransactionEver);

            var firstOutputEver       = firstTransactionEver.Outputs.First();
            var firstScriptPubKeyEver = firstOutputEver.ScriptPubKey;

            Console.WriteLine(firstScriptPubKeyEver); // 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG


            var firstBitcoinAddressEver = firstScriptPubKeyEver.GetDestinationAddress(Network.Main);

            Console.WriteLine(firstBitcoinAddressEver == null); // True

            var firstPubKeyEver = firstScriptPubKeyEver.GetDestinationPublicKeys().First();

            Console.WriteLine(firstPubKeyEver); // 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f

            var key = new Key();

            Console.WriteLine("Pay to public key : " + key.PubKey.ScriptPubKey);
            Console.WriteLine();
            Console.WriteLine("Pay to public key hash : " + key.PubKey.Hash.ScriptPubKey);


            /* MUSTISIG */

            Key bob     = new Key();
            Key alice   = new Key();
            Key satoshi = new Key();

            scriptPubKey = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, new PubKey[]
            {
                bob.PubKey,
                alice.PubKey,
                satoshi.PubKey
            });

            Console.WriteLine(scriptPubKey);

            var received = new Transaction();

            received.Outputs.Add(new TxOut(Money.Coins(1.0m), scriptPubKey));

            Coin coin = received.Outputs.AsCoins().First();

            BitcoinAddress     nico     = new Key().PubKey.GetAddress(Network.Main);
            TransactionBuilder builder  = new TransactionBuilder();
            Transaction        unsigned =
                builder
                .AddCoins(coin)
                .Send(nico, Money.Coins(1.0m))
                .BuildTransaction(sign: false);

            Transaction aliceSigned =
                builder
                .AddCoins(coin)
                .AddKeys(alice)
                .SignTransaction(unsigned);

            Transaction bobSigned =
                builder
                .AddCoins(coin)
                .AddKeys(bob)
                .SignTransaction(unsigned);

            Transaction fullySigned =
                builder
                .AddCoins(coin)
                .CombineSignatures(aliceSigned, bobSigned);

            Console.WriteLine(fullySigned);

            /* Pay to Script Hash */

            Console.WriteLine(scriptPubKey);
            Console.WriteLine(scriptPubKey.Hash.ScriptPubKey);

            Script redeemScript =
                PayToMultiSigTemplate
                .Instance
                .GenerateScriptPubKey(2, new[] { bob.PubKey, alice.PubKey, satoshi.PubKey });

            received = new Transaction();
            //Pay to the script hash
            received.Outputs.Add(new TxOut(Money.Coins(1.0m), redeemScript.Hash));

            ScriptCoin scriptCoin = received.Outputs.AsCoins().First().ToScriptCoin(redeemScript);

            // P2SH(P2WPKH)

            Console.WriteLine(key.PubKey.WitHash.ScriptPubKey.Hash.ScriptPubKey);

            Console.WriteLine(key.PubKey.ScriptPubKey.WitHash.ScriptPubKey.Hash.ScriptPubKey);

            // Arbitrary

            BitcoinAddress address   = BitcoinAddress.Create("1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB");
            var            birth     = Encoding.UTF8.GetBytes("18/07/1988");
            var            birthHash = Hashes.Hash256(birth);

            redeemScript = new Script(
                "OP_IF "
                + "OP_HASH256 " + Op.GetPushOp(birthHash.ToBytes()) + " OP_EQUAL " +
                "OP_ELSE "
                + address.ScriptPubKey + " " +
                "OP_ENDIF");

            var tx = new Transaction();

            tx.Outputs.Add(new TxOut(Money.Parse("0.0001"), redeemScript.Hash));
            scriptCoin = tx.Outputs.AsCoins().First().ToScriptCoin(redeemScript);

            //Create spending transaction
            Transaction spending = new Transaction();

            spending.AddInput(new TxIn(new OutPoint(tx, 0)));

            ////Option 1 : Spender knows my birthdate
            Op     pushBirthdate = Op.GetPushOp(birth);
            Op     selectIf      = OpcodeType.OP_1; //go to if
            Op     redeemBytes   = Op.GetPushOp(redeemScript.ToBytes());
            Script scriptSig     = new Script(pushBirthdate, selectIf, redeemBytes);

            spending.Inputs[0].ScriptSig = scriptSig;

            //Verify the script pass
            var result = spending
                         .Inputs
                         .AsIndexedInputs()
                         .First()
                         .VerifyScript(tx.Outputs[0].ScriptPubKey);

            Console.WriteLine(result); // True
            ///////////

            ////Option 2 : Spender knows my private key
            BitcoinSecret secret     = new BitcoinSecret("...");
            var           sig        = spending.SignInput(secret, scriptCoin);
            var           p2pkhProof = PayToPubkeyHashTemplate
                                       .Instance
                                       .GenerateScriptSig(sig, secret.PrivateKey.PubKey);

            selectIf  = OpcodeType.OP_0; //go to else
            scriptSig = p2pkhProof + selectIf + redeemBytes;
            spending.Inputs[0].ScriptSig = scriptSig;


            //Verify the script pass
            result = spending
                     .Inputs
                     .AsIndexedInputs()
                     .First()
                     .VerifyScript(tx.Outputs[0].ScriptPubKey);
            Console.WriteLine(result);
            ///////////

            Console.ReadLine();
        }
コード例 #22
0
        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.GetAccount();
            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());
        }
コード例 #23
0
        private (TransactionBuildContext, HdAccount, Script) GetWithdrawalTransactionBuildContext(string receivingAddress, string walletName, Money amount, Money feeAmount, bool subtractFeeFromAmount)
        {
            Guard.NotEmpty(receivingAddress, nameof(receivingAddress));
            Guard.NotEmpty(walletName, nameof(walletName));
            Guard.NotNull(amount, nameof(amount));

            Wallet.Wallet wallet = this.GetWallet(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).Any(s => s.Address == receivingAddress || s.Bech32Address == 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).Any(s => s.Address == receivingAddress || s.Bech32Address == 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.");
            }

            Script destination = null;

            if (BitcoinWitPubKeyAddress.IsValid(receivingAddress, this.network, out Exception _))
            {
                destination = new BitcoinWitPubKeyAddress(receivingAddress, wallet.Network).ScriptPubKey;
            }
            else
            {
                // Send the money to the receiving address.
                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 transaction 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, SubtractFeeFromAmount = subtractFeeFromAmount
                                           } }.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;

            return(context, coldAccount, destination);
        }
コード例 #24
0
        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 keyvalue 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.GetAccount();

            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 exception)
            {
                throw new RPCServerException(RPCErrorCode.RPC_WALLET_UNLOCK_NEEDED, exception.Message);
            }
            catch (WalletException exception)
            {
                throw new RPCServerException(RPCErrorCode.RPC_WALLET_ERROR, exception.Message);
            }
            catch (NotImplementedException exception)
            {
                throw new RPCServerException(RPCErrorCode.RPC_MISC_ERROR, exception.Message);
            }
        }
コード例 #25
0
        public async Task CanComputeCrowdfundModel()
        {
            using (var tester = ServerTester.Create())
            {
                await tester.StartAsync();

                var user = tester.NewAccount();
                user.GrantAccess();
                user.RegisterDerivationScheme("BTC");
                await 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, "save").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, "save").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, "save").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);
                });
            }
        }
コード例 #26
0
ファイル: PSBTTests.cs プロジェクト: xinghuo1321/btcpayserver
        public async Task CanPlayWithPSBT()
        {
            using (var tester = ServerTester.Create())
            {
                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 <WalletsController>();
                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.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel()
                {
                    SigningContext = new SigningContextModel()
                    {
                        PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady))
                    }
                }).AssertViewModelAsync <WalletPSBTReadyViewModel>();

                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.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel()
                {
                    SigningContext = new SigningContextModel()
                    {
                        PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady))
                    }
                }).AssertViewModelAsync <WalletPSBTReadyViewModel>();

                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);
                var redirect = Assert.IsType <RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, psbtReady, command: "broadcast"));
                Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName);

                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.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel()
                {
                    SigningContext = new SigningContextModel(signedPSBT)
                })).AssertViewModel <WalletPSBTReadyViewModel>();
                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);
                redirect = Assert.IsType <RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, ready, command: "broadcast"));
                Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName);
            }
        }
コード例 #27
0
        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(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());
        }
コード例 #28
0
        public async Task CanHaveLTCOnlyStore()
        {
            using (var tester = CreateServerTester())
            {
                tester.ActivateLTC();
                await tester.StartAsync();

                var user = tester.NewAccount();
                user.GrantAccess();
                user.RegisterDerivationScheme("LTC");

                // First we try payment with a merchant having only BTC
                var invoice = user.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price             = 500,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true
                }, Facade.Merchant);

                Assert.Single(invoice.CryptoInfo);
                Assert.Equal("LTC", invoice.CryptoInfo[0].CryptoCode);
                Assert.True(invoice.PaymentCodes.ContainsKey("LTC"));
                Assert.True(invoice.SupportedTransactionCurrencies.ContainsKey("LTC"));
                Assert.True(invoice.SupportedTransactionCurrencies["LTC"].Enabled);
                Assert.True(invoice.PaymentSubtotals.ContainsKey("LTC"));
                Assert.True(invoice.PaymentTotals.ContainsKey("LTC"));
                var cashCow        = tester.LTCExplorerNode;
                var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
                var firstPayment   = Money.Coins(0.1m);
                cashCow.SendToAddress(invoiceAddress, firstPayment);
                TestUtils.Eventually(() =>
                {
                    invoice = user.BitPay.GetInvoice(invoice.Id);
                    Assert.Equal(firstPayment, invoice.CryptoInfo[0].Paid);
                });

                Assert.Single(invoice.CryptoInfo); // Only BTC should be presented

                var controller = tester.PayTester.GetController <UIInvoiceController>(null);
                var checkout   =
                    (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id)
                                                          .GetAwaiter().GetResult()).Value;
                Assert.Single(checkout.AvailableCryptos);
                Assert.Equal("LTC", checkout.CryptoCode);

                //////////////////////

                // Despite it is called BitcoinAddress it should be LTC because BTC is not available
                Assert.Null(invoice.BitcoinAddress);
                Assert.NotEqual(1.0m, invoice.Rate);
                Assert.NotEqual(invoice.BtcDue, invoice.CryptoInfo[0].Due); // Should be BTC rate
                cashCow.SendToAddress(invoiceAddress, invoice.CryptoInfo[0].Due);

                TestUtils.Eventually(() =>
                {
                    invoice = user.BitPay.GetInvoice(invoice.Id);
                    Assert.Equal("paid", invoice.Status);
                    checkout = (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id)
                                                                     .GetAwaiter().GetResult()).Value;
                    Assert.Equal("paid", checkout.Status);
                });
            }
        }
コード例 #29
0
        private static void Send(string password, string walletName, string wallet, string outPoint)
        {
            string walletFilePath = @"Wallets\";

            NBitcoin.BitcoinExtKey privateKey = null;
            try
            {
                Safe loadedSafe = Safe.Load(password, walletFilePath + walletName + ".json");
                for (int i = 0; i < 10; i++)
                {
                    if (loadedSafe.GetAddress(i).ToString() == wallet)
                    {
                        Write("Enter private key: ");
                        privateKey = new BitcoinExtKey(ReadLine());
                        if (!privateKey.Equals(loadedSafe.FindPrivateKey(loadedSafe.GetAddress(i))))
                        {
                            WriteLine("Wrong private key!");
                            return;
                        }
                        break;
                    }
                }
            }
            catch
            {
                WriteLine("Wrong wallet or password!");
                return;
            }
            QBitNinjaClient client          = new QBitNinjaClient(Network.TestNet);
            var             balance         = client.GetBalance(BitcoinAddress.Create(wallet), false).Result;
            OutPoint        outPointToSpend = null;

            foreach (var entry in balance.Operations)
            {
                foreach (var coin in entry.ReceivedCoins)
                {
                    if (coin.Outpoint.ToString().Substring(0, coin.Outpoint.ToString().Length - 2) == outPoint)
                    {
                        outPointToSpend = coin.Outpoint;
                        break;
                    }
                }
            }
            var transaction = new Transaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut = outPointToSpend
            });
            Write("Enter address to send to: ");
            string addressToSentTo        = ReadLine();
            var    hallOfTheMakersAddress = BitcoinAddress.Create(addressToSentTo);

            Write("Enter amount to send: ");
            decimal amountToSend         = decimal.Parse(ReadLine());
            TxOut   hallOfTheMakersTxOut = new TxOut()
            {
                Value        = new Money(amountToSend, MoneyUnit.BTC),
                ScriptPubKey = hallOfTheMakersAddress.ScriptPubKey
            };

            Write("Enter amount to get back: ");
            decimal amountToGetBack = decimal.Parse(ReadLine());
            TxOut   changeBackTxOut = new TxOut()
            {
                Value        = new Money(amountToGetBack, MoneyUnit.BTC),
                ScriptPubKey = privateKey.ScriptPubKey
            };

            transaction.Outputs.Add(hallOfTheMakersTxOut);
            transaction.Outputs.Add(changeBackTxOut);
            Write("Enter message: ");
            var message = ReadLine();
            var bytes   = Encoding.UTF8.GetBytes(message);

            transaction.Outputs.Add(new TxOut()
            {
                Value        = Money.Zero,
                ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes)
            });
            transaction.Inputs[0].ScriptSig = privateKey.ScriptPubKey;
            transaction.Sign(privateKey, false);
            BroadcastResponse broadcastResponse = client.Broadcast(transaction).Result;

            if (broadcastResponse.Success)
            {
                Console.WriteLine("Transaction send!");
            }
            else
            {
                Console.WriteLine("something went worng!:-(");
            }
        }
コード例 #30
0
 public void DeserializeBech()
 {
     BitcoinAddress.Create("bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", Network.Main);
 }