/// <summary>
        /// Creates a money request. 
        /// </summary>
        public MoneyRequest CreateMoneyRequest(MoneyRequest moneyRequest)
        {
            JSONObject json = _api.Post(SettingsHelper.MoneyRequestsUri, moneyRequest.ToJSON(), ContentType.JSON);
            moneyRequest = new MoneyRequest(json);

            return moneyRequest;
        }
        /// <summary>
        /// Gets a money request. 
        /// </summary>
        public MoneyRequest GetMoneyRequest(Int32 moneyRequestId)
        {
            JSONObject json = _api.Get(SettingsHelper.MoneyRequestsUri + "/" + moneyRequestId.ToString());
            MoneyRequest moneyRequest = new MoneyRequest(json);

            return moneyRequest;
        }
예제 #3
0
        public void ProcessRequest(HttpContext context)
        {
            System.IO.StreamReader sm = new System.IO.StreamReader(context.Request.InputStream);
            string MoneyRequestStr    = sm.ReadToEnd();

            try
            {
                context.Response.ContentType = "text/plain";
//                 MoneyRequest _MoneyRequest = new MoneyRequest();
//                 _MoneyRequest.UserId = 100;
                MoneyRequest _MoneyRequest  = LitJson.JsonMapper.ToObject <MoneyRequest>(MoneyRequestStr);
                MoneyReturn  newMoneyReturn = new MoneyReturn();
                newMoneyReturn.code = 0;
                newMoneyReturn.msg  = "";
                {
                    var prams = new List <DbParameter>();
                    prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("dwUserID", _MoneyRequest.UserId));
                    prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("dwBindType", _MoneyRequest.BankAccountType));
                    prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("szBindId", _MoneyRequest.AccountNo));
                    prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("szBindAccountName", _MoneyRequest.BankAccountUsername));
                    prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("szBindBankName", _MoneyRequest.Bank));
                    prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("strErrorDescribe", "suss"));

                    FacadeManage.aideAccountsFacade.DataProvider.GetDbHelper().RunProc("GSP_GP_AccountBankBind", prams);
                }
                context.Response.Write(LitJson.JsonMapper.ToJson(newMoneyReturn));
            }
            catch (Exception exp)
            {
                MoneyReturn newMoneyReturn = new MoneyReturn();
                newMoneyReturn.code = 1;
                newMoneyReturn.msg  = exp.Message.ToString();
                context.Response.Write(MoneyRequestStr + "--" + LitJson.JsonMapper.ToJson(newMoneyReturn));
            }
        }
예제 #4
0
        /// <summary>
        /// 执行资金操作
        /// </summary>
        public virtual MoneyResponse ExecuteMoney(MoneyRequest request)
        {
            var sw = new Stopwatch();

            sw.Start();
            try
            {
                var result = NetAgent.UploadData(request.Url, request.PostData, Encoding.UTF8, null, new Dictionary <string, string>()
                {
                    { "Content-Type", "application/json" },
                    { "Content-Language", request.Language.ToString() },
                    { "X-Forwarded-IP", IPAgent.IP }
                });

                if (result.StartsWith("Error:"))
                {
                    throw new Exception(result);
                }
                return(new MoneyResponse(request, result, (int)sw.ElapsedMilliseconds));
            }
            catch (Exception ex)
            {
                return(new MoneyResponse(request, ex.Message, (int)sw.ElapsedMilliseconds, true));
            }
        }
예제 #5
0
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            try
            {
                string          UserId           = context.Request.QueryString["UserId"];
                MoneyRequestMgr _MoneyRequestMgr = new MoneyRequestMgr();

                DataSet             ds            = FacadeManage.aideAccountsFacade.DataProvider.GetDbHelper().ExecuteDataset(CommandType.Text, "select* from AccountBankBind where UserID=" + UserId);
                List <MoneyRequest> MoneyRequests = new List <MoneyRequest>();
                for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
                {
                    MoneyRequest newMoneyRequest = new MoneyRequest();
                    newMoneyRequest.UserId              = Convert.ToInt32(UserId);
                    newMoneyRequest.BankAccountType     = Convert.ToInt32(ds.Tables[0].Rows[i]["BindType"]);
                    newMoneyRequest.AccountNo           = ds.Tables[0].Rows[i]["BindId"].ToString();
                    newMoneyRequest.BankAccountUsername = ds.Tables[0].Rows[i]["BindAccountName"].ToString();
                    newMoneyRequest.Bank = ds.Tables[0].Rows[i]["BindBankName"].ToString();
                    _MoneyRequestMgr.Banks.Add(newMoneyRequest);
                }
                context.Response.Write(LitJson.JsonMapper.ToJson(_MoneyRequestMgr));
            }
            catch (Exception exp)
            {
                MoneyRequestMgr _MoneyRequestMgr = new MoneyRequestMgr();
                _MoneyRequestMgr.code = 1;
                _MoneyRequestMgr.msg  = exp.Message.ToString();
                context.Response.Write(LitJson.JsonMapper.ToJson(_MoneyRequestMgr));
            }
        }
        public async Task <ActionResult> DeleteConfirmed(int id)
        {
            MoneyRequest moneyRequest = await db.MoneyRequests.FindAsync(id);

            db.MoneyRequests.Remove(moneyRequest);
            await db.SaveChangesAsync();

            return(RedirectToAction("Index"));
        }
        public async Task <ActionResult> Edit([Bind(Include = "ID,UserID,UserName,RequestID,RequestBalance,RequestDate")] MoneyRequest moneyRequest)
        {
            if (ModelState.IsValid)
            {
                db.Entry(moneyRequest).State = EntityState.Modified;
                await db.SaveChangesAsync();

                return(RedirectToAction("Index"));
            }
            return(View(moneyRequest));
        }
        public async Task <ActionResult> Edit(int?id)
        {
            if (id == null)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }
            MoneyRequest moneyRequest = await db.MoneyRequests.FindAsync(id);

            if (moneyRequest == null)
            {
                return(HttpNotFound());
            }
            return(View(moneyRequest));
        }
예제 #9
0
        public async Task <IActionResult> WithdrawlMoney([FromBody] MoneyRequest bodyPayload)
        {
            /*
             * The body of that request should contain an amount and a UserId(Not BankUserId, not SkatUserId)
             * Subtract (if possible) the amount from that users account. Throw an error otherwise.
             */

            if (bodyPayload.Amount <= 0)
            {
                return(BadRequest("Amount cannot be 0 or negative."));
            }

            using (var connection = _databaseContext.Connection)
            {
                using (var transaction = connection.BeginTransaction())
                {
                    try
                    {
                        var result = await connection.QueryFirstOrDefaultAsync("select UserId from BankUser where Id = @Id", new { Id = bodyPayload.UserId }, transaction);

                        var withdrawMoneyresult = await connection.ExecuteAsync
                                                  (
                            @"
                            DECLARE @amount INT
                            SELECT 
                                @amount = Amount
                            FROM	
                                Account WHERE BankUserId = @BankUserId 
                            IF @amount > 0  and @amount > @substract
                            UPDATE 
                                Account SET Amount = @amount - @substract WHERE BankUserId = @BankUserId", new { BankUserId = result.UserId, substract = bodyPayload.Amount }, transaction
                                                  );

                        if (withdrawMoneyresult != 1)
                        {
                            new Exception();                           // Change to custom exception
                        }
                        transaction.Commit();
                        return(Ok("Sucesful withdrawal"));
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();
                        return(NotFound("Withdrawal failed."));
                    }
                }
            }
        }
예제 #10
0
    public static BuildTransactionResult BuildChangelessTransaction(Wallet wallet, BitcoinAddress address, SmartLabel labels, FeeRate feeRate, IEnumerable <SmartCoin> coins, bool tryToSign = true)
    {
        var intent = new PaymentIntent(
            address,
            MoneyRequest.CreateAllRemaining(subtractFee: true),
            labels);

        var txRes = wallet.BuildTransaction(
            wallet.Kitchen.SaltSoup(),
            intent,
            FeeStrategy.CreateFromFeeRate(feeRate),
            allowUnconfirmed: true,
            coins.Select(coin => coin.OutPoint),
            tryToSign: tryToSign);

        return(txRes);
    }
        //public async Task<ActionResult> Create([Bind(Include = "ID,UserID,UserName,RequestID,RequestBalance,RequestDate")] MoneyRequest moneyRequest)
        public async Task <ActionResult> Create(MoneyRequestViewModel model)
        {
            var usersID      = User.Identity.GetUserId();
            var usersName    = User.Identity.GetUserName();
            var moneyRequest = new MoneyRequest
            {
                ID             = model.ID,
                UserID         = usersID,
                UserName       = usersName,
                RequestID      = model.RequestID,
                RequestBalance = model.RequestBalance,
                RequestDate    = DateTime.Now.ToString("MM/dd/yyyy")
            };

            if (ModelState.IsValid)
            {
                db.MoneyRequests.Add(moneyRequest);
                await db.SaveChangesAsync();

                return(RedirectToAction("Index"));
            }

            return(View(moneyRequest));
        }
예제 #12
0
        public async Task SendTestsAsync()
        {
            (string password, IRPCClient rpc, Network network, _, ServiceConfiguration serviceConfiguration, BitcoinStore bitcoinStore, Backend.Global global) = await Common.InitializeTestEnvironmentAsync(RegTestFixture, 1);

            bitcoinStore.IndexStore.NewFilter += Common.Wallet_NewFilterProcessed;
            // Create the services.
            // 1. Create connection service.
            var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);

            nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());

            // 2. Create mempool service.

            Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();

            node.Behaviors.Add(bitcoinStore.CreateUntrustedP2pBehavior());

            // 3. Create wasabi synchronizer service.
            var synchronizer = new WasabiSynchronizer(rpc.Network, bitcoinStore, new Uri(RegTestFixture.BackendEndPoint), null);

            // 4. Create key manager service.
            var keyManager = KeyManager.CreateNew(out _, password);

            // 5. Create wallet service.
            var workDir = Tests.Common.GetWorkDir();

            CachedBlockProvider blockProvider = new CachedBlockProvider(
                new P2pBlockProvider(nodes, null, synchronizer, serviceConfiguration, network),
                bitcoinStore.BlockRepository);

            var walletManager = new WalletManager(network, new WalletDirectories(workDir));

            walletManager.RegisterServices(bitcoinStore, synchronizer, nodes, serviceConfiguration, synchronizer, blockProvider);

            // Get some money, make it confirm.
            var key  = keyManager.GetNextReceiveKey("foo label", out _);
            var key2 = keyManager.GetNextReceiveKey("foo label", out _);
            var txId = await rpc.SendToAddressAsync(key.GetP2wpkhAddress(network), Money.Coins(1m));

            Assert.NotNull(txId);
            await rpc.GenerateAsync(1);

            var txId2 = await rpc.SendToAddressAsync(key2.GetP2wpkhAddress(network), Money.Coins(1m));

            Assert.NotNull(txId2);
            await rpc.GenerateAsync(1);

            try
            {
                Interlocked.Exchange(ref Common.FiltersProcessedByWalletCount, 0);
                nodes.Connect();                                                                              // Start connection service.
                node.VersionHandshake();                                                                      // Start mempool service.
                synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.

                // Wait until the filter our previous transaction is present.
                var blockCount = await rpc.GetBlockCountAsync();

                await Common.WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), blockCount);

                var wallet = await walletManager.AddAndStartWalletAsync(keyManager);

                var broadcaster = new TransactionBroadcaster(network, bitcoinStore, synchronizer, nodes, walletManager, rpc);

                var waitCount = 0;
                while (wallet.Coins.Sum(x => x.Amount) == Money.Zero)
                {
                    await Task.Delay(1000);

                    waitCount++;
                    if (waitCount >= 21)
                    {
                        Logger.LogInfo("Funding transaction to the wallet did not arrive.");
                        return;                         // Very rarely this test fails. I have no clue why. Probably because all these RegTests are interconnected, anyway let's not bother the CI with it.
                    }
                }

                var scp  = new Key().ScriptPubKey;
                var res2 = wallet.BuildTransaction(password, new PaymentIntent(scp, Money.Coins(0.05m), label: "foo"), FeeStrategy.CreateFromConfirmationTarget(5), allowUnconfirmed: false);

                Assert.NotNull(res2.Transaction);
                Assert.Single(res2.OuterWalletOutputs);
                Assert.Equal(scp, res2.OuterWalletOutputs.Single().ScriptPubKey);
                Assert.Single(res2.InnerWalletOutputs);
                Assert.True(res2.Fee > Money.Satoshis(2 * 100));                 // since there is a sanity check of 2sat/vb in the server
                Assert.InRange(res2.FeePercentOfSent, 0, 1);
                Assert.Single(res2.SpentCoins);
                var spentCoin = Assert.Single(res2.SpentCoins);
                Assert.Contains(new[] { key.P2wpkhScript, key2.P2wpkhScript }, x => x == spentCoin.ScriptPubKey);
                Assert.Equal(Money.Coins(1m), res2.SpentCoins.Single().Amount);
                Assert.False(res2.SpendsUnconfirmed);

                await broadcaster.SendTransactionAsync(res2.Transaction);

                Assert.Contains(res2.InnerWalletOutputs.Single(), wallet.Coins);

                #region Basic

                Script receive      = keyManager.GetNextReceiveKey("Basic", out _).P2wpkhScript;
                Money  amountToSend = wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) / 2;
                var    res          = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true);

                foreach (SmartCoin coin in res.SpentCoins)
                {
                    Assert.False(coin.CoinJoinInProgress);
                    Assert.True(coin.Confirmed);
                    Assert.Null(coin.SpenderTransactionId);
                    Assert.True(coin.Unspent);
                }

                Assert.Equal(2, res.InnerWalletOutputs.Count());
                Assert.Empty(res.OuterWalletOutputs);
                var activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive);
                var changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive);

                Assert.Equal(receive, activeOutput.ScriptPubKey);
                Assert.Equal(amountToSend, activeOutput.Amount);
                if (res.SpentCoins.Sum(x => x.Amount) - activeOutput.Amount == res.Fee)                 // this happens when change is too small
                {
                    Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == activeOutput.Amount);
                    Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                }
                Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}");
                Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %");
                Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}");
                Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"TxId: {res.Transaction.GetHash()}");

                var foundReceive = false;
                Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                await broadcaster.SendTransactionAsync(res.Transaction);

                #endregion Basic

                #region SubtractFeeFromAmount

                receive      = keyManager.GetNextReceiveKey("SubtractFeeFromAmount", out _).P2wpkhScript;
                amountToSend = wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) / 3;
                res          = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, subtractFee: true, label: "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true);

                Assert.Equal(2, res.InnerWalletOutputs.Count());
                Assert.Empty(res.OuterWalletOutputs);
                activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive);
                changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive);

                Assert.Equal(receive, activeOutput.ScriptPubKey);
                Assert.Equal(amountToSend - res.Fee, activeOutput.Amount);
                Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == changeOutput.Amount);
                Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}");
                Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %");
                Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}");
                Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"TxId: {res.Transaction.GetHash()}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend - res.Fee, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion SubtractFeeFromAmount

                #region LowFee

                res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true);

                Assert.Equal(2, res.InnerWalletOutputs.Count());
                Assert.Empty(res.OuterWalletOutputs);
                activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive);
                changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive);

                Assert.Equal(receive, activeOutput.ScriptPubKey);
                Assert.Equal(amountToSend, activeOutput.Amount);
                Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == changeOutput.Amount);
                Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}");
                Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %");
                Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}");
                Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"TxId: {res.Transaction.GetHash()}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion LowFee

                #region MediumFee

                res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), FeeStrategy.OneDayConfirmationTargetStrategy, allowUnconfirmed: true);

                Assert.Equal(2, res.InnerWalletOutputs.Count());
                Assert.Empty(res.OuterWalletOutputs);
                activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive);
                changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive);

                Assert.Equal(receive, activeOutput.ScriptPubKey);
                Assert.Equal(amountToSend, activeOutput.Amount);
                Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == changeOutput.Amount);
                Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}");
                Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %");
                Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}");
                Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"TxId: {res.Transaction.GetHash()}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion MediumFee

                #region HighFee

                res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), FeeStrategy.TwentyMinutesConfirmationTargetStrategy, allowUnconfirmed: true);

                Assert.Equal(2, res.InnerWalletOutputs.Count());
                Assert.Empty(res.OuterWalletOutputs);
                activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive);
                changeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey != receive);

                Assert.Equal(receive, activeOutput.ScriptPubKey);
                Assert.Equal(amountToSend, activeOutput.Amount);
                Assert.Contains(res.Transaction.Transaction.Outputs, x => x.Value == changeOutput.Amount);
                Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}");
                Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %");
                Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}");
                Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"Change Output: {changeOutput.Amount.ToString(false, true)} {changeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"TxId: {res.Transaction.GetHash()}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                Assert.InRange(res.Fee, Money.Zero, res.Fee);
                Assert.InRange(res.Fee, res.Fee, res.Fee);

                await broadcaster.SendTransactionAsync(res.Transaction);

                #endregion HighFee

                #region MaxAmount

                receive = keyManager.GetNextReceiveKey("MaxAmount", out _).P2wpkhScript;

                res = wallet.BuildTransaction(password, new PaymentIntent(receive, MoneyRequest.CreateAllRemaining(), "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true);

                Assert.Single(res.InnerWalletOutputs);
                Assert.Empty(res.OuterWalletOutputs);
                activeOutput = res.InnerWalletOutputs.Single();

                Assert.Equal(receive, activeOutput.ScriptPubKey);

                Assert.Single(res.Transaction.Transaction.Outputs);
                var maxBuiltTxOutput = res.Transaction.Transaction.Outputs.Single();
                Assert.Equal(receive, maxBuiltTxOutput.ScriptPubKey);
                Assert.Equal(wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) - res.Fee, maxBuiltTxOutput.Value);

                await broadcaster.SendTransactionAsync(res.Transaction);

                #endregion MaxAmount

                #region InputSelection

                receive = keyManager.GetNextReceiveKey("InputSelection", out _).P2wpkhScript;

                var inputCountBefore = res.SpentCoins.Count();

                res = wallet.BuildTransaction(password, new PaymentIntent(receive, MoneyRequest.CreateAllRemaining(), "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy,
                                              allowUnconfirmed: true,
                                              allowedInputs: wallet.Coins.Where(x => !x.Unavailable).Select(x => x.OutPoint).Take(1));

                Assert.Single(res.InnerWalletOutputs);
                Assert.Empty(res.OuterWalletOutputs);
                activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive);

                Assert.True(inputCountBefore >= res.SpentCoins.Count());
                Assert.Equal(res.SpentCoins.Count(), res.Transaction.Transaction.Inputs.Count);

                Assert.Equal(receive, activeOutput.ScriptPubKey);
                Logger.LogInfo($"{nameof(res.Fee)}: {res.Fee}");
                Logger.LogInfo($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %");
                Logger.LogInfo($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}");
                Logger.LogInfo($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogInfo($"TxId: {res.Transaction.GetHash()}");

                Assert.Single(res.Transaction.Transaction.Outputs);

                res = wallet.BuildTransaction(password, new PaymentIntent(receive, MoneyRequest.CreateAllRemaining(), "foo"), FeeStrategy.SevenDaysConfirmationTargetStrategy,
                                              allowUnconfirmed: true,
                                              allowedInputs: new[] { res.SpentCoins.Select(x => x.OutPoint).First() });

                Assert.Single(res.InnerWalletOutputs);
                Assert.Empty(res.OuterWalletOutputs);
                activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive);

                Assert.Single(res.Transaction.Transaction.Inputs);
                Assert.Single(res.Transaction.Transaction.Outputs);
                Assert.Single(res.SpentCoins);

                #endregion InputSelection

                #region Labeling

                Script receive2 = keyManager.GetNextReceiveKey("foo", out _).P2wpkhScript;
                res = wallet.BuildTransaction(password, new PaymentIntent(receive2, MoneyRequest.CreateAllRemaining(), "my label"), FeeStrategy.SevenDaysConfirmationTargetStrategy, allowUnconfirmed: true);

                Assert.Single(res.InnerWalletOutputs);
                Assert.Equal("foo, my label", res.InnerWalletOutputs.Single().Label);

                amountToSend = wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) / 3;
                res          = wallet.BuildTransaction(
                    password,
                    new PaymentIntent(
                        new DestinationRequest(new Key(), amountToSend, label: "outgoing"),
                        new DestinationRequest(new Key(), amountToSend, label: "outgoing2")),
                    FeeStrategy.SevenDaysConfirmationTargetStrategy,
                    allowUnconfirmed: true);

                Assert.Single(res.InnerWalletOutputs);
                Assert.Equal(2, res.OuterWalletOutputs.Count());
                IEnumerable <string> change = res.InnerWalletOutputs.Single().Label.Labels;
                Assert.Contains("outgoing", change);
                Assert.Contains("outgoing2", change);

                await broadcaster.SendTransactionAsync(res.Transaction);

                IEnumerable <SmartCoin> unconfirmedCoins      = wallet.Coins.Where(x => x.Height == Height.Mempool).ToArray();
                IEnumerable <string>    unconfirmedCoinLabels = unconfirmedCoins.SelectMany(x => x.Label.Labels).ToArray();
                Assert.Contains("outgoing", unconfirmedCoinLabels);
                Assert.Contains("outgoing2", unconfirmedCoinLabels);
                IEnumerable <string> allKeyLabels = keyManager.GetKeys().SelectMany(x => x.Label.Labels);
                Assert.Contains("outgoing", allKeyLabels);
                Assert.Contains("outgoing2", allKeyLabels);

                Interlocked.Exchange(ref Common.FiltersProcessedByWalletCount, 0);
                await rpc.GenerateAsync(1);

                await Common.WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), 1);

                var bestHeight = new Height(bitcoinStore.SmartHeaderChain.TipHeight);
                IEnumerable <string> confirmedCoinLabels = wallet.Coins.Where(x => x.Height == bestHeight).SelectMany(x => x.Label.Labels);
                Assert.Contains("outgoing", confirmedCoinLabels);
                Assert.Contains("outgoing2", confirmedCoinLabels);
                allKeyLabels = keyManager.GetKeys().SelectMany(x => x.Label.Labels);
                Assert.Contains("outgoing", allKeyLabels);
                Assert.Contains("outgoing2", allKeyLabels);

                #endregion Labeling

                #region AllowedInputsDisallowUnconfirmed

                inputCountBefore = res.SpentCoins.Count();

                receive = keyManager.GetNextReceiveKey("AllowedInputsDisallowUnconfirmed", out _).P2wpkhScript;

                var allowedInputs = wallet.Coins.Where(x => !x.Unavailable).Select(x => x.OutPoint).Take(1);
                var toSend        = new PaymentIntent(receive, MoneyRequest.CreateAllRemaining(), "fizz");

                // covers:
                // disallow unconfirmed with allowed inputs
                res = wallet.BuildTransaction(password, toSend, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, false, allowedInputs: allowedInputs);

                activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive);
                Assert.Single(res.InnerWalletOutputs);
                Assert.Empty(res.OuterWalletOutputs);

                Assert.Equal(receive, activeOutput.ScriptPubKey);
                Logger.LogDebug($"{nameof(res.Fee)}: {res.Fee}");
                Logger.LogDebug($"{nameof(res.FeePercentOfSent)}: {res.FeePercentOfSent} %");
                Logger.LogDebug($"{nameof(res.SpendsUnconfirmed)}: {res.SpendsUnconfirmed}");
                Logger.LogDebug($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Logger.LogDebug($"TxId: {res.Transaction.GetHash()}");

                Assert.True(inputCountBefore >= res.SpentCoins.Count());
                Assert.False(res.SpendsUnconfirmed);

                Assert.Single(res.Transaction.Transaction.Inputs);
                Assert.Single(res.Transaction.Transaction.Outputs);
                Assert.Single(res.SpentCoins);

                Assert.True(inputCountBefore >= res.SpentCoins.Count());
                Assert.Equal(res.SpentCoins.Count(), res.Transaction.Transaction.Inputs.Count);

                #endregion AllowedInputsDisallowUnconfirmed

                #region CustomChange

                // covers:
                // customchange
                // feePc > 1
                var k1 = new Key();
                var k2 = new Key();
                res = wallet.BuildTransaction(
                    password,
                    new PaymentIntent(
                        new DestinationRequest(k1, MoneyRequest.CreateChange()),
                        new DestinationRequest(k2, Money.Coins(0.0003m), label: "outgoing")),
                    FeeStrategy.TwentyMinutesConfirmationTargetStrategy);

                Assert.Contains(k1.ScriptPubKey, res.OuterWalletOutputs.Select(x => x.ScriptPubKey));
                Assert.Contains(k2.ScriptPubKey, res.OuterWalletOutputs.Select(x => x.ScriptPubKey));

                #endregion CustomChange

                #region FeePcHigh

                res = wallet.BuildTransaction(
                    password,
                    new PaymentIntent(new Key(), Money.Coins(0.0003m), label: "outgoing"),
                    FeeStrategy.TwentyMinutesConfirmationTargetStrategy);

                Assert.True(res.FeePercentOfSent > 1);

                var newChangeK = keyManager.GenerateNewKey("foo", KeyState.Clean, isInternal: true);
                res = wallet.BuildTransaction(
                    password,
                    new PaymentIntent(
                        new DestinationRequest(newChangeK.P2wpkhScript, MoneyRequest.CreateChange(), "boo"),
                        new DestinationRequest(new Key(), Money.Coins(0.0003m), label: "outgoing")),
                    FeeStrategy.TwentyMinutesConfirmationTargetStrategy);

                Assert.True(res.FeePercentOfSent > 1);
                Assert.Single(res.OuterWalletOutputs);
                Assert.Single(res.InnerWalletOutputs);
                SmartCoin changeRes = res.InnerWalletOutputs.Single();
                Assert.Equal(newChangeK.P2wpkhScript, changeRes.ScriptPubKey);
                Assert.Equal(newChangeK.Label, changeRes.Label);
                Assert.Equal(KeyState.Clean, newChangeK.KeyState);                 // Still clean, because the tx wasn't yet propagated.

                #endregion FeePcHigh
            }
            finally
            {
                bitcoinStore.IndexStore.NewFilter -= Common.Wallet_NewFilterProcessed;
                await walletManager.RemoveAndStopAllAsync(CancellationToken.None);

                // Dispose wasabi synchronizer service.
                if (synchronizer is { })
예제 #13
0
        public async Task BuildTransactionValidationsTestAsync()
        {
            (string password, IRPCClient rpc, Network network, Coordinator coordinator, ServiceConfiguration serviceConfiguration, BitcoinStore bitcoinStore, Backend.Global global) = await Common.InitializeTestEnvironmentAsync(RegTestFixture, 1);

            // Create the services.
            // 1. Create connection service.
            var nodes = new NodesGroup(global.Config.Network, requirements: Constants.NodeRequirements);

            nodes.ConnectedNodes.Add(await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync());

            // 2. Create mempool service.

            Node node = await RegTestFixture.BackendRegTestNode.CreateNewP2pNodeAsync();

            node.Behaviors.Add(bitcoinStore.CreateUntrustedP2pBehavior());

            // 3. Create wasabi synchronizer service.
            var synchronizer = new WasabiSynchronizer(rpc.Network, bitcoinStore, new Uri(RegTestFixture.BackendEndPoint), null);

            // 4. Create key manager service.
            var keyManager = KeyManager.CreateNew(out _, password);

            // 5. Create wallet service.
            var workDir          = Common.GetWorkDir();
            var blocksFolderPath = Path.Combine(workDir, "Blocks", network.ToString());
            CachedBlockProvider blockProvider = new CachedBlockProvider(
                new P2pBlockProvider(nodes, null, synchronizer, serviceConfiguration, network),
                new FileSystemBlockRepository(blocksFolderPath, network));

            using var wallet           = Wallet.CreateAndRegisterServices(network, bitcoinStore, keyManager, synchronizer, nodes, workDir, serviceConfiguration, synchronizer, blockProvider);
            wallet.NewFilterProcessed += Common.Wallet_NewFilterProcessed;

            var scp = new Key().ScriptPubKey;

            var validIntent   = new PaymentIntent(scp, Money.Coins(1));
            var invalidIntent = new PaymentIntent(
                new DestinationRequest(scp, Money.Coins(10 * 1000 * 1000)),
                new DestinationRequest(scp, Money.Coins(12 * 1000 * 1000)));

            Assert.Throws <OverflowException>(() => new PaymentIntent(
                                                  new DestinationRequest(scp, Money.Satoshis(long.MaxValue)),
                                                  new DestinationRequest(scp, Money.Satoshis(long.MaxValue)),
                                                  new DestinationRequest(scp, Money.Satoshis(5))));

            Logger.TurnOff();
            Assert.Throws <ArgumentNullException>(() => wallet.BuildTransaction(null, null, FeeStrategy.CreateFromConfirmationTarget(4)));

            // toSend cannot have a null element
            Assert.Throws <ArgumentNullException>(() => wallet.BuildTransaction(null, new PaymentIntent(new[] { (DestinationRequest)null }), FeeStrategy.CreateFromConfirmationTarget(0)));

            // toSend cannot have a zero element
            Assert.Throws <ArgumentException>(() => wallet.BuildTransaction(null, new PaymentIntent(Array.Empty <DestinationRequest>()), FeeStrategy.SevenDaysConfirmationTargetStrategy));

            // feeTarget has to be in the range 0 to 1008
            Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validIntent, FeeStrategy.CreateFromConfirmationTarget(-10)));
            Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validIntent, FeeStrategy.CreateFromConfirmationTarget(2000)));

            // toSend amount sum has to be in range 0 to 2099999997690000
            Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, invalidIntent, FeeStrategy.TwentyMinutesConfirmationTargetStrategy));

            // toSend negative sum amount
            Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, new PaymentIntent(scp, Money.Satoshis(-10000)), FeeStrategy.TwentyMinutesConfirmationTargetStrategy));

            // toSend negative operation amount
            Assert.Throws <ArgumentOutOfRangeException>(() => wallet.BuildTransaction(
                                                            null,
                                                            new PaymentIntent(
                                                                new DestinationRequest(scp, Money.Satoshis(20000)),
                                                                new DestinationRequest(scp, Money.Satoshis(-10000))),
                                                            FeeStrategy.TwentyMinutesConfirmationTargetStrategy));

            // allowedInputs cannot be empty
            Assert.Throws <ArgumentException>(() => wallet.BuildTransaction(null, validIntent, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, allowedInputs: Array.Empty <OutPoint>()));

            // "Only one element can contain the AllRemaining flag.
            Assert.Throws <ArgumentException>(() => wallet.BuildTransaction(
                                                  password,
                                                  new PaymentIntent(
                                                      new DestinationRequest(scp, MoneyRequest.CreateAllRemaining(), "zero"),
                                                      new DestinationRequest(scp, MoneyRequest.CreateAllRemaining(), "zero")),
                                                  FeeStrategy.SevenDaysConfirmationTargetStrategy,
                                                  false));

            // Get some money, make it confirm.
            var txId = await rpc.SendToAddressAsync(keyManager.GetNextReceiveKey("foo", out _).GetP2wpkhAddress(network), Money.Coins(1m));

            // Generate some coins
            await rpc.GenerateAsync(2);

            try
            {
                nodes.Connect();                                                                              // Start connection service.
                node.VersionHandshake();                                                                      // Start mempool service.
                synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.

                // Wait until the filter our previous transaction is present.
                var blockCount = await rpc.GetBlockCountAsync();

                await Common.WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), blockCount);

                using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
                {
                    await wallet.StartAsync(cts.Token);                     // Initialize wallet service.
                }

                // subtract Fee from amount index with no enough money
                var operations = new PaymentIntent(
                    new DestinationRequest(scp, Money.Coins(1m), subtractFee: true),
                    new DestinationRequest(scp, Money.Coins(0.5m)));
                Assert.Throws <InsufficientBalanceException>(() => wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, false));

                // No enough money (only one confirmed coin, no unconfirmed allowed)
                operations = new PaymentIntent(scp, Money.Coins(1.5m));
                Assert.Throws <InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy));

                // No enough money (only one confirmed coin, unconfirmed allowed)
                Assert.Throws <InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, true));

                // Add new money with no confirmation
                var txId2 = await rpc.SendToAddressAsync(keyManager.GetNextReceiveKey("bar", out _).GetP2wpkhAddress(network), Money.Coins(2m));

                await Task.Delay(1000);                 // Wait tx to arrive and get processed.

                // Enough money (one confirmed coin and one unconfirmed coin, unconfirmed are NOT allowed)
                Assert.Throws <InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, false));

                // Enough money (one unconfirmed coin, unconfirmed are allowed)
                var btx       = wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, true);
                var spentCoin = Assert.Single(btx.SpentCoins);
                Assert.False(spentCoin.Confirmed);

                // Enough money (one confirmed coin and one unconfirmed coin, unconfirmed are allowed)
                operations = new PaymentIntent(scp, Money.Coins(2.5m));
                btx        = wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, true);
                Assert.Equal(2, btx.SpentCoins.Count());
                Assert.Equal(1, btx.SpentCoins.Count(c => c.Confirmed));
                Assert.Equal(1, btx.SpentCoins.Count(c => !c.Confirmed));

                // Only one operation with AllRemainingFlag

                Assert.Throws <ArgumentException>(() => wallet.BuildTransaction(
                                                      null,
                                                      new PaymentIntent(
                                                          new DestinationRequest(scp, MoneyRequest.CreateAllRemaining()),
                                                          new DestinationRequest(scp, MoneyRequest.CreateAllRemaining())),
                                                      FeeStrategy.TwentyMinutesConfirmationTargetStrategy));

                Logger.TurnOn();

                operations = new PaymentIntent(scp, Money.Coins(0.5m));
                btx        = wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy);
            }
            finally
            {
                await wallet.StopAsync(CancellationToken.None);

                // Dispose wasabi synchronizer service.
                if (synchronizer is { })
예제 #14
0
        public string BuildTransaction(PaymentInfo[] payments, TxoRef[] coins, int feeTarget, string password = null)
        {
            Guard.NotNull(nameof(payments), payments);
            Guard.NotNull(nameof(coins), coins);
            Guard.InRangeAndNotNull(nameof(feeTarget), feeTarget, 2, Constants.SevenDaysConfirmationTarget);
            password = Guard.Correct(password);

            AssertWalletIsLoaded();
            var sync    = Global.Synchronizer;
            var payment = new PaymentIntent(payments.Select(p =>
                                                            new DestinationRequest(p.Sendto.ScriptPubKey, MoneyRequest.Create(p.Amount, p.SubtractFee), new SmartLabel(p.Label))));
            var feeStrategy = FeeStrategy.CreateFromConfirmationTarget(feeTarget);
            var result      = Global.WalletService.BuildTransaction(
                password,
                payment,
                feeStrategy,
                allowUnconfirmed: true,
                allowedInputs: coins);
            var smartTx = result.Transaction;

            return(smartTx.Transaction.ToHex());
        }
예제 #15
0
        public async Task <bool> BuildTransaction(string password)
        {
            try
            {
                IsBusy   = true;
                password = Guard.Correct(password);
                Memo     = Memo.Trim(',', ' ').Trim();

                var selectedCoinViewModels = SendAmountViewModel.CoinList.CoinList.Where(cvm => cvm.IsSelected);
                var selectedCoinReferences = selectedCoinViewModels.Select(cvm => cvm.Model.OutPoint).ToList();

                if (!selectedCoinReferences.Any())
                {
                    //SetWarningMessage("No coins are selected to spend.");
                    return(false);
                }

                BitcoinAddress address;
                try
                {
                    address = BitcoinAddress.Create(Address.Trim(), Global.Network);
                }
                catch (FormatException)
                {
                    // SetWarningMessage("Invalid address.");
                    return(false);
                }

                var amount = Money.Zero;

                var requests = new List <DestinationRequest>();

                MoneyRequest moneyRequest;
                if (SendAmountViewModel.IsMax)
                {
                    moneyRequest = MoneyRequest.CreateAllRemaining(subtractFee: true);
                }
                else
                {
                    if (!Money.TryParse(SendAmountViewModel.AmountText, out amount) || amount == Money.Zero)
                    {
                        // SetWarningMessage($"Invalid amount.");
                        return(false);
                    }

                    if (amount == selectedCoinViewModels.Sum(x => x.Amount))
                    {
                        // NotificationHelpers.Warning("Looks like you want to spend whole coins. Try Max button instead.", "");
                        return(false);
                    }
                    moneyRequest = MoneyRequest.Create(amount, subtractFee: false);
                }

                if (SendAmountViewModel.FeeRate is null || SendAmountViewModel.FeeRate.SatoshiPerByte < 1)
                {
                    return(false);
                }

                var feeStrategy = FeeStrategy.CreateFromFeeRate(SendAmountViewModel.FeeRate);

                var smartLabel = new SmartLabel(Memo);
                var activeDestinationRequest = new DestinationRequest(address, moneyRequest, smartLabel);
                requests.Add(activeDestinationRequest);
                var intent = new PaymentIntent(requests);

                var result = await Task.Run(() => Global.Wallet.BuildTransaction(
                                                password,
                                                intent,
                                                feeStrategy,
                                                allowUnconfirmed: true,
                                                allowedInputs: selectedCoinReferences));

                SmartTransaction signedTransaction = result.Transaction;

                await Global.TransactionBroadcaster.SendTransactionAsync(signedTransaction); // put this on non-ui theread?

                return(true);
            }
            catch (InsufficientBalanceException ex)
            {
                Money needed = ex.Minimum - ex.Actual;
                Logger.LogDebug(ex);
                //SetWarningMessage($"Not enough coins selected. You need an estimated {needed.ToString(false, true)} BTC more to make this transaction.");
            }
            catch (Exception ex)
            {
                Logger.LogDebug(ex);
                //SetWarningMessage(ex.ToTypeMessageString());
            }
            finally
            {
                IsBusy = false;
            }
            return(false);
        }
예제 #16
0
 public PaymentIntent(IDestination destination, MoneyRequest amount, SmartLabel?label = null) : this(new DestinationRequest(destination, amount, label))
 {
 }
 public DestinationRequest(Script scriptPubKey, Money amount, bool subtractFee = false, SmartLabel?label = null) : this(scriptPubKey, MoneyRequest.Create(amount, subtractFee), label)
 {
 }
예제 #18
0
 public MoneyResponse ExecuteMoney(string url, MoneyRequest request)
 {
     throw new NotImplementedException();
 }
 public DestinationRequest(IDestination destination, Money amount, bool subtractFee = false, SmartLabel?label = null) : this(destination, MoneyRequest.Create(amount, subtractFee), label)
 {
 }
 public DestinationRequest(IDestination destination, MoneyRequest amount, SmartLabel?label = null)
 {
     Destination = destination;
     Amount      = amount;
     Label       = label ?? SmartLabel.Empty;
 }
 public DestinationRequest(Script scriptPubKey, MoneyRequest amount, SmartLabel?label = null) : this(scriptPubKey.GetDestination(), amount, label)
 {
 }
예제 #22
0
        public async Task <object> SendTransactionAsync(PaymentInfo[] payments, TxoRef[] coins, int feeTarget, string password = null)
        {
            Guard.NotNull(nameof(payments), payments);
            Guard.NotNull(nameof(coins), coins);
            Guard.InRangeAndNotNull(nameof(feeTarget), feeTarget, 2, Constants.SevenDaysConfirmationTarget);
            password = Guard.Correct(password);

            AssertWalletIsLoaded();
            var sync    = Global.Synchronizer;
            var payment = new PaymentIntent(payments.Select(p =>
                                                            new DestinationRequest(p.Sendto.ScriptPubKey, MoneyRequest.Create(p.Amount, p.SubtractFee), new SmartLabel(p.Label))));
            var feeStrategy = FeeStrategy.CreateFromConfirmationTarget(feeTarget);
            var result      = Global.WalletService.BuildTransaction(
                password,
                payment,
                feeStrategy,
                allowUnconfirmed: true,
                allowedInputs: coins);
            var smartTx = result.Transaction;

            // dequeue the coins we are going to spend
            var toDequeue = Global.WalletService.Coins
                            .Where(x => x.CoinJoinInProgress && coins.Contains(x.GetTxoRef()))
                            .ToArray();

            if (toDequeue.Any())
            {
                await Global.ChaumianClient.DequeueCoinsFromMixAsync(toDequeue, DequeueReason.TransactionBuilding).ConfigureAwait(false);
            }

            await Global.TransactionBroadcaster.SendTransactionAsync(smartTx).ConfigureAwait(false);

            return(new
            {
                txid = smartTx.Transaction.GetHash(),
                tx = smartTx.Transaction.ToHex()
            });
        }
예제 #23
0
 public MoneyResponse(MoneyRequest request, string json, int duration, bool isException = false) : base(json, duration, isException)
 {
     Request = request;
 }
예제 #24
0
        protected SendControlViewModel(Wallet wallet, string title)
            : base(title)
        {
            Global = Locator.Current.GetService <Global>();
            Wallet = wallet;

            LabelSuggestion            = new SuggestLabelViewModel();
            BuildTransactionButtonText = DoButtonText;

            this.ValidateProperty(x => x.Address, ValidateAddress);
            this.ValidateProperty(x => x.CustomChangeAddress, ValidateCustomChangeAddress);
            this.ValidateProperty(x => x.Password, ValidatePassword);
            this.ValidateProperty(x => x.UserFeeText, ValidateUserFeeText);

            ResetUi();
            SetAmountWatermark(Money.Zero);

            CoinList = new CoinListViewModel(Wallet, displayCommonOwnershipWarning: true);

            Observable.FromEventPattern(CoinList, nameof(CoinList.SelectionChanged))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => SetFeesAndTexts());

            _minMaxFeeTargetsEqual = this.WhenAnyValue(x => x.MinimumFeeTarget, x => x.MaximumFeeTarget, (x, y) => x == y)
                                     .ToProperty(this, x => x.MinMaxFeeTargetsEqual, scheduler: RxApp.MainThreadScheduler);

            SetFeeTargetLimits();
            FeeTarget        = Global.UiConfig.FeeTarget;
            FeeDisplayFormat = (FeeDisplayFormat)(Enum.ToObject(typeof(FeeDisplayFormat), Global.UiConfig.FeeDisplayFormat) ?? FeeDisplayFormat.SatoshiPerByte);
            SetFeesAndTexts();

            this.WhenAnyValue(x => x.AmountText)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x =>
            {
                if (Money.TryParse(x.TrimStart('~', ' '), out Money amountBtc))
                {
                    SetAmountWatermark(amountBtc);
                }
                else
                {
                    SetAmountWatermark(Money.Zero);
                }

                SetFees();
            });

            AmountKeyUpCommand = ReactiveCommand.Create((KeyEventArgs key) =>
            {
                var amount = AmountText;
                if (IsMax)
                {
                    SetFeesAndTexts();
                }
                else
                {
                    // Correct amount
                    Regex digitsOnly    = new Regex(@"[^\d,.]");
                    string betterAmount = digitsOnly.Replace(amount, "");                     // Make it digits , and . only.

                    betterAmount          = betterAmount.Replace(',', '.');
                    int countBetterAmount = betterAmount.Count(x => x == '.');
                    if (countBetterAmount > 1)                     // Do not enable typing two dots.
                    {
                        var index = betterAmount.IndexOf('.', betterAmount.IndexOf('.') + 1);
                        if (index > 0)
                        {
                            betterAmount = betterAmount.Substring(0, index);
                        }
                    }
                    var dotIndex = betterAmount.IndexOf('.');
                    if (dotIndex != -1 && betterAmount.Length - dotIndex > 8)                     // Enable max 8 decimals.
                    {
                        betterAmount = betterAmount.Substring(0, dotIndex + 1 + 8);
                    }

                    if (betterAmount != amount)
                    {
                        AmountText = betterAmount;
                    }
                }
            });

            this.WhenAnyValue(x => x.IsBusy, x => x.IsHardwareBusy)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => BuildTransactionButtonText = IsHardwareBusy
                                                ? WaitingForHardwareWalletButtonTextString
                                                : IsBusy
                                                        ? DoingButtonText
                                                        : DoButtonText);

            this.WhenAnyValue(x => x.FeeTarget)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                IsSliderFeeUsed = true;
                SetFeesAndTexts();
            });

            this.WhenAnyValue(x => x.IsSliderFeeUsed)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(enabled => FeeControlOpacity = enabled ? 1 : 0.5);                     // Give the control the disabled feeling. Real Disable it not a solution as we have to detect if the slider is moved.

            MaxCommand = ReactiveCommand.Create(() => IsMax = !IsMax, outputScheduler: RxApp.MainThreadScheduler);

            this.WhenAnyValue(x => x.IsMax)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                if (IsMax)
                {
                    SetFeesAndTexts();

                    LabelToolTip = "Spending whole coins does not generate change, thus labeling is unnecessary.";
                }
                else
                {
                    AmountText = "0.0";

                    LabelToolTip = "Who can link this transaction to you? E.g.: \"Max, BitPay\"";
                }
            });

            // Triggering the detection of same address values.
            this.WhenAnyValue(x => x.Address)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => this.RaisePropertyChanged(nameof(CustomChangeAddress)));

            this.WhenAnyValue(x => x.CustomChangeAddress)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => this.RaisePropertyChanged(nameof(Address)));

            this.WhenAnyValue(x => x.IsCustomChangeAddressVisible)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                this.RaisePropertyChanged(nameof(Address));
                this.RaisePropertyChanged(nameof(CustomChangeAddress));
            });

            FeeRateCommand = ReactiveCommand.Create(ChangeFeeRateDisplay, outputScheduler: RxApp.MainThreadScheduler);

            OnAddressPasteCommand = ReactiveCommand.Create((BitcoinUrlBuilder url) => OnAddressPaste(url));

            BuildTransactionCommand = ReactiveCommand.CreateFromTask(async() =>
            {
                try
                {
                    IsBusy = true;
                    MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.BuildingTransaction);

                    var label             = new SmartLabel(LabelSuggestion.Label);
                    LabelSuggestion.Label = label;
                    if (!IsMax && label.IsEmpty)
                    {
                        NotificationHelpers.Warning("Observers are required.", "");
                        return;
                    }

                    var selectedCoinViewModels = CoinList.Coins.Where(cvm => cvm.IsSelected);
                    var selectedCoinReferences = selectedCoinViewModels.Select(cvm => cvm.Model.OutPoint).ToList();

                    if (!selectedCoinReferences.Any())
                    {
                        NotificationHelpers.Warning("No coins are selected to spend.", "");
                        return;
                    }

                    BitcoinAddress address;
                    try
                    {
                        address = BitcoinAddress.Create(Address, Global.Network);
                    }
                    catch (FormatException)
                    {
                        NotificationHelpers.Warning("Invalid address.", "");
                        return;
                    }

                    var requests = new List <DestinationRequest>();

                    if (IsCustomChangeAddressVisible && !string.IsNullOrWhiteSpace(CustomChangeAddress))
                    {
                        try
                        {
                            var customChangeAddress = BitcoinAddress.Create(CustomChangeAddress, Global.Network);

                            if (customChangeAddress == address)
                            {
                                NotificationHelpers.Warning("The active address and the change address cannot be the same.", "");
                                return;
                            }

                            requests.Add(new DestinationRequest(customChangeAddress, MoneyRequest.CreateChange(subtractFee: true), label));
                        }
                        catch (FormatException)
                        {
                            NotificationHelpers.Warning("Invalid custom change address.", "");
                            return;
                        }
                    }

                    MoneyRequest moneyRequest;
                    if (IsMax)
                    {
                        moneyRequest = MoneyRequest.CreateAllRemaining(subtractFee: true);
                    }
                    else
                    {
                        if (!Money.TryParse(AmountText, out Money amount) || amount == Money.Zero)
                        {
                            NotificationHelpers.Warning("Invalid amount.");
                            return;
                        }

                        if (amount == selectedCoinViewModels.Sum(x => x.Amount))
                        {
                            NotificationHelpers.Warning("Looks like you want to spend whole coins. Try Max button instead.", "");
                            return;
                        }
                        moneyRequest = MoneyRequest.Create(amount, subtractFee: false);
                    }

                    if (FeeRate is null || FeeRate.SatoshiPerByte < 1)
                    {
                        NotificationHelpers.Warning("Invalid fee rate.", "");
                        return;
                    }

                    var feeStrategy = FeeStrategy.CreateFromFeeRate(FeeRate);

                    var activeDestinationRequest = new DestinationRequest(address, moneyRequest, label);
                    requests.Add(activeDestinationRequest);

                    var intent = new PaymentIntent(requests);
                    try
                    {
                        MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.DequeuingSelectedCoins);
                        OutPoint[] toDequeue = selectedCoinViewModels.Where(x => x.CoinJoinInProgress).Select(x => x.Model.OutPoint).ToArray();
                        if (toDequeue != null && toDequeue.Any())
                        {
                            await Wallet.ChaumianClient.DequeueCoinsFromMixAsync(toDequeue, DequeueReason.TransactionBuilding);
                        }
                    }
                    catch
                    {
                        NotificationHelpers.Error("Cannot spend mixing coins.", "");
                        return;
                    }
                    finally
                    {
                        MainWindowViewModel.Instance.StatusBar.TryRemoveStatus(StatusType.DequeuingSelectedCoins);
                    }

                    if (!Wallet.KeyManager.IsWatchOnly)
                    {
                        try
                        {
                            PasswordHelper.GetMasterExtKey(Wallet.KeyManager, Password, out string compatiblityPasswordUsed);                             // We could use TryPassword but we need the exception.
                            if (compatiblityPasswordUsed != null)
                            {
                                Password = compatiblityPasswordUsed;                                 // Overwrite the password for BuildTransaction function.
                                NotificationHelpers.Warning(PasswordHelper.CompatibilityPasswordWarnMessage);
                            }
                        }
                        catch (SecurityException ex)
                        {
                            NotificationHelpers.Error(ex.Message, "");
                            return;
                        }
                        catch (Exception ex)
                        {
                            Logger.LogError(ex);
                            NotificationHelpers.Error(ex.ToUserFriendlyString());
                            return;
                        }
                    }

                    await BuildTransaction(Password, intent, feeStrategy, allowUnconfirmed: true, allowedInputs: selectedCoinReferences);
                }
                catch (InsufficientBalanceException ex)
                {
                    Money needed = ex.Minimum - ex.Actual;
                    NotificationHelpers.Error($"Not enough coins selected. You need an estimated {needed.ToString(false, true)} BTC more to make this transaction.", "");
                }
                catch (HttpRequestException ex)
                {
                    NotificationHelpers.Error(ex.ToUserFriendlyString());
                    Logger.LogError(ex);
                }
                catch (Exception ex)
                {
                    NotificationHelpers.Error(ex.ToUserFriendlyString(), sender: Wallet);
                    Logger.LogError(ex);
                }
                finally
                {
                    MainWindowViewModel.Instance.StatusBar.TryRemoveStatus(StatusType.BuildingTransaction, StatusType.SigningTransaction, StatusType.BroadcastingTransaction);
                    IsBusy = false;
                }
            },
                                                                     this.WhenAny(x => x.IsMax, x => x.AmountText, x => x.Address, x => x.IsBusy,
                                                                                  (isMax, amount, address, busy) => (isMax.Value || !string.IsNullOrWhiteSpace(amount.Value)) && !string.IsNullOrWhiteSpace(Address) && !IsBusy)
                                                                     .ObserveOn(RxApp.MainThreadScheduler));

            UserFeeTextKeyUpCommand = ReactiveCommand.Create((KeyEventArgs key) =>
            {
                IsSliderFeeUsed = !IsCustomFee;
                SetFeesAndTexts();
            });

            FeeSliderClickedCommand = ReactiveCommand.Create((PointerPressedEventArgs mouse) => IsSliderFeeUsed = true);

            HighLightFeeSliderCommand = ReactiveCommand.Create((bool entered) =>
            {
                if (IsSliderFeeUsed)
                {
                    return;
                }

                FeeControlOpacity = entered ? 0.8 : 0.5;
            });

            Observable
            .Merge(MaxCommand.ThrownExceptions)
            .Merge(FeeRateCommand.ThrownExceptions)
            .Merge(OnAddressPasteCommand.ThrownExceptions)
            .Merge(BuildTransactionCommand.ThrownExceptions)
            .Merge(UserFeeTextKeyUpCommand.ThrownExceptions)
            .Merge(FeeSliderClickedCommand.ThrownExceptions)
            .Merge(HighLightFeeSliderCommand.ThrownExceptions)
            .Merge(AmountKeyUpCommand.ThrownExceptions)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex =>
            {
                NotificationHelpers.Error(ex.ToUserFriendlyString());
                Logger.LogError(ex);
            });
        }
예제 #25
0
        public void ProcessRequest(HttpContext context)
        {
            try
            {
                MoneyReturn newMoneyReturn = new MoneyReturn();
                context.Response.ContentType = "text/plain";
                System.IO.StreamReader sm    = new System.IO.StreamReader(context.Request.InputStream);
                string       MoneyRequestStr = sm.ReadToEnd();
                MoneyRequest _MoneyRequest   = LitJson.JsonMapper.ToObject <MoneyRequest>(MoneyRequestStr);
                _MoneyRequest.Money *= 100;
//                 string sql = string.Format("SELECT UserID FROM AccontPayChangeMoney WHERE UserID={0} and ChangeStatus=0", _MoneyRequest.UserId);
//                 {
//                     DataSet ds = FacadeManage.aideAccountsFacade.DataProvider.GetDbHelper().ExecuteDataset(CommandType.Text, sql);
//                     if (ds.Tables[0].Rows.Count > 0)
//                     {
//                         newMoneyReturn.code = 1;
//                         newMoneyReturn.msg = "不能重复申请";
//                         context.Response.Write(LitJson.JsonMapper.ToJson(newMoneyReturn));
//                         return;
//                     }
//                 }
                {
                    DataSet ds = FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().ExecuteDataset(CommandType.Text,
                                                                                                           "select Score from GameScoreInfo where UserID=" + _MoneyRequest.UserId);
                    if (ds.Tables[0].Rows.Count > 0)
                    {
                        int src = Convert.ToInt32(ds.Tables[0].Rows[0]["Score"]);
                        if (src < _MoneyRequest.Money)
                        {
                            newMoneyReturn.code = 2;
                            newMoneyReturn.msg  = "余额不足";
                            context.Response.Write(LitJson.JsonMapper.ToJson(newMoneyReturn));
                            return;
                        }
                        else
                        {
                            string MyUpdate = "Update GameScoreInfo set Score=Score-" + ((int)_MoneyRequest.Money).ToString() +
                                              " where UserID=" + _MoneyRequest.UserId;
                            FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().ExecuteDataset(CommandType.Text, MyUpdate);
                        }
                    }
                }
                var prams = new List <DbParameter>();
                prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("dwUserID", _MoneyRequest.UserId));
                prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("szRequestTime", DateTime.Now.ToString()));
                prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("szCheckTime", DateTime.Now.ToString()));
                prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("fAmount", _MoneyRequest.Money));
                prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("dwBankAccountType", _MoneyRequest.BankAccountType));
                prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("dwChangeStatus", 0));
                prams.Add(FacadeManage.aideTreasureFacade.DataProvider.GetDbHelper().MakeInParam("strErrorDescribe", ""));
                object payId = FacadeManage.aideAccountsFacade.DataProvider.GetDbHelper().ExecuteScalar(CommandType.StoredProcedure, "GSP_GP_AccountPayChangeMoney", prams.ToArray());

                var sqlBuilder = new StringBuilder();
                sqlBuilder.Append("EXEC NET_PM_RecordScoreChanged ")
                .AppendFormat("'{0}',", _MoneyRequest.UserId)
                .AppendFormat("'{0}',", -_MoneyRequest.Money)
                .AppendFormat("'{0}',", payId)
                .AppendFormat("'{0}',", "GET_MONEY")
                .AppendFormat("'{0}',", "提现")
                .AppendFormat("'{0}'", "ChangedNumber为提现申请表中的PayId");
                FacadeManage.aideAccountsFacade.DataProvider.GetDbHelper().ExecuteNonQuery(sqlBuilder.ToString());

                newMoneyReturn.code = 0;
                newMoneyReturn.msg  = "";

                context.Response.Write(LitJson.JsonMapper.ToJson(newMoneyReturn));
            }
            catch (Exception exp)
            {
                MoneyReturn newMoneyReturn = new MoneyReturn();
                newMoneyReturn.code = 1;
                newMoneyReturn.msg  = exp.Message.ToString();
                context.Response.Write(LitJson.JsonMapper.ToJson(newMoneyReturn));
            }
        }
예제 #26
0
        protected SendControlViewModel(Wallet wallet, string title)
            : base(title)
        {
            Global = Locator.Current.GetService <Global>();
            Wallet = wallet;

            LabelSuggestion             = new SuggestLabelViewModel();
            _buildTransactionButtonText = DoButtonText;

            this.ValidateProperty(x => x.Address, ValidateAddress);
            this.ValidateProperty(x => x.CustomChangeAddress, ValidateCustomChangeAddress);
            this.ValidateProperty(x => x.Password, ValidatePassword);
            this.ValidateProperty(x => x.UserFeeText, ValidateUserFeeText);

            ResetUi();

            CoinList = new CoinListViewModel(Wallet, Global.Config, Global.UiConfig, displayCommonOwnershipWarning: true);

            Observable.FromEventPattern(CoinList, nameof(CoinList.SelectionChanged))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => SetFeesAndTexts());

            _minMaxFeeTargetsEqual = this.WhenAnyValue(x => x.MinimumFeeTarget, x => x.MaximumFeeTarget, (x, y) => x == y)
                                     .ToProperty(this, x => x.MinMaxFeeTargetsEqual, scheduler: RxApp.MainThreadScheduler);

            SetFeeTargetLimits();
            FeeTarget        = Global.UiConfig.FeeTarget;
            FeeDisplayFormat = (FeeDisplayFormat)(Enum.ToObject(typeof(FeeDisplayFormat), Global.UiConfig.FeeDisplayFormat) ?? FeeDisplayFormat.SatoshiPerByte);
            SetFeesAndTexts();

            this.WhenAnyValue(x => x.AmountText)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x =>
            {
                if (Money.TryParse(x.TrimStart('~', ' '), out Money amountBtc))
                {
                    SetAmountWatermark(amountBtc);
                }
                else
                {
                    SetAmountWatermark(Money.Zero);
                }

                SetFees();
            });

            AmountKeyUpCommand = ReactiveCommand.Create((KeyEventArgs key) =>
            {
                if (IsMax)
                {
                    SetFeesAndTexts();
                }
                else if (BitcoinInput.TryCorrectAmount(AmountText, out var betterAmount))
                {
                    AmountText = betterAmount;
                }
            });

            this.WhenAnyValue(x => x.IsBusy, x => x.IsHardwareBusy)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => BuildTransactionButtonText = IsHardwareBusy
                                                ? WaitingForHardwareWalletButtonTextString
                                                : IsBusy
                                                        ? DoingButtonText
                                                        : DoButtonText);

            Observable
            .Merge(this.WhenAnyValue(x => x.FeeTarget).Select(_ => true))
            .Merge(this.WhenAnyValue(x => x.IsEstimateAvailable).Select(_ => true))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                IsSliderFeeUsed = IsEstimateAvailable;
                SetFeesAndTexts();
            });

            this.WhenAnyValue(x => x.IsSliderFeeUsed)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(enabled => FeeControlOpacity = enabled ? 1 : 0.5);                     // Give the control the disabled feeling. Real Disable it not a solution as we have to detect if the slider is moved.

            MaxCommand = ReactiveCommand.Create(() => IsMax = !IsMax, outputScheduler: RxApp.MainThreadScheduler);

            this.WhenAnyValue(x => x.IsMax)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                if (IsMax)
                {
                    SetFeesAndTexts();

                    LabelToolTip = "Spending whole coins does not generate change, thus labeling is unnecessary.";
                }
                else
                {
                    AmountText = "0.0";

                    LabelToolTip = "Who can link this transaction to you? E.g.: \"Max, BitPay\"";
                }
            });

            // Triggering the detection of same address values.
            this.WhenAnyValue(x => x.Address)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => this.RaisePropertyChanged(nameof(CustomChangeAddress)));

            this.WhenAnyValue(x => x.CustomChangeAddress)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => this.RaisePropertyChanged(nameof(Address)));

            this.WhenAnyValue(x => x.IsCustomChangeAddressVisible)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                this.RaisePropertyChanged(nameof(Address));
                this.RaisePropertyChanged(nameof(CustomChangeAddress));
            });

            FeeRateCommand = ReactiveCommand.Create(ChangeFeeRateDisplay, outputScheduler: RxApp.MainThreadScheduler);

            OnAddressPasteCommand = ReactiveCommand.Create((BitcoinUrlBuilder url) => OnAddressPaste(url));

            BuildTransactionCommand = ReactiveCommand.CreateFromTask(async() =>
            {
                try
                {
                    IsBusy = true;
                    MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.BuildingTransaction);

                    var label             = new SmartLabel(LabelSuggestion.Label);
                    LabelSuggestion.Label = label;
                    if (!IsMax && label.IsEmpty)
                    {
                        NotificationHelpers.Warning("Label is required.", "");
                        return;
                    }

                    var selectedCoinViewModels = CoinList.Coins.Where(cvm => cvm.IsSelected);
                    var selectedCoinReferences = selectedCoinViewModels.Select(cvm => cvm.Model.OutPoint).ToList();

                    if (!selectedCoinReferences.Any())
                    {
                        NotificationHelpers.Warning("No coins are selected to spend.", "");
                        return;
                    }

                    BitcoinAddress address;
                    try
                    {
                        address = BitcoinAddress.Create(Address, Global.Network);
                    }
                    catch (FormatException)
                    {
                        NotificationHelpers.Warning("Invalid address.", "");
                        return;
                    }

                    var requests = new List <DestinationRequest>();

                    if (IsCustomChangeAddressVisible && !string.IsNullOrWhiteSpace(CustomChangeAddress))
                    {
                        try
                        {
                            var customChangeAddress = BitcoinAddress.Create(CustomChangeAddress, Global.Network);

                            if (customChangeAddress == address)
                            {
                                NotificationHelpers.Warning("The active address and the change address cannot be the same.", "");
                                return;
                            }

                            requests.Add(new DestinationRequest(customChangeAddress, MoneyRequest.CreateChange(subtractFee: true), label));
                        }
                        catch (FormatException)
                        {
                            NotificationHelpers.Warning("Invalid custom change address.", "");
                            return;
                        }
                    }

                    MoneyRequest moneyRequest;
                    if (IsMax)
                    {
                        moneyRequest = MoneyRequest.CreateAllRemaining(subtractFee: true);
                    }
                    else
                    {
                        if (!Money.TryParse(AmountText, out Money amount) || amount == Money.Zero)
                        {
                            NotificationHelpers.Warning("Invalid amount.");
                            return;
                        }

                        if (amount == selectedCoinViewModels.Sum(x => x.Amount))
                        {
                            NotificationHelpers.Warning("Looks like you want to spend whole coins. Try Max button instead.", "");
                            return;
                        }
                        moneyRequest = MoneyRequest.Create(amount, subtractFee: false);
                    }

                    if (FeeRate is null || FeeRate.SatoshiPerByte < 1)
                    {
                        NotificationHelpers.Warning("Invalid fee rate.", "");
                        return;
                    }

                    var feeStrategy = FeeStrategy.CreateFromFeeRate(FeeRate);

                    var activeDestinationRequest = new DestinationRequest(address, moneyRequest, label);
                    requests.Add(activeDestinationRequest);

                    var intent = new PaymentIntent(requests);
                    try
                    {
                        MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusType.DequeuingSelectedCoins);
                        OutPoint[] toDequeue = selectedCoinViewModels.Where(x => x.CoinJoinInProgress).Select(x => x.Model.OutPoint).ToArray();
                        if (toDequeue is { } && toDequeue.Any())
                        {
                            await Wallet.ChaumianClient.DequeueCoinsFromMixAsync(toDequeue, DequeueReason.TransactionBuilding);
                        }
                    }
                    catch
                    {
                        NotificationHelpers.Error("Cannot spend mixing coins.", "");
                        return;
                    }
                    finally
                    {
                        MainWindowViewModel.Instance.StatusBar.TryRemoveStatus(StatusType.DequeuingSelectedCoins);
                    }

                    if (!Wallet.KeyManager.IsWatchOnly)
                    {
                        try
                        {
                            PasswordHelper.GetMasterExtKey(Wallet.KeyManager, Password, out var compatiblityPasswordUsed);                             // We could use TryPassword but we need the exception.
                            if (compatiblityPasswordUsed is { })
                            {
                                Password = compatiblityPasswordUsed;                                 // Overwrite the password for BuildTransaction function.
                                NotificationHelpers.Warning(PasswordHelper.CompatibilityPasswordWarnMessage);
                            }
                        }
                        catch (SecurityException ex)
                        {
                            NotificationHelpers.Error(ex.Message, "");
                            return;
                        }
                        catch (Exception ex)
                        {
                            Logger.LogError(ex);
                            NotificationHelpers.Error(ex.ToUserFriendlyString());
                            return;
                        }
                    }
예제 #27
0
 public MoneyResponse ExecuteMoney(MoneyRequest request) => throw new NotImplementedException();