示例#1
0
        public ActionResult ShowTransaction(int id)
        {
            LnTransaction t = new LnTransaction();

            using (CoinpanicContext db = new CoinpanicContext())
            {
                var tr = db.LnTransactions.AsNoTracking().FirstOrDefault(tid => tid.TransactionId == id);
                if (tr != null)
                {
                    t = tr;
                }
            }
            return(View(t));
        }
示例#2
0
        /// <summary>
        /// Notify web clients via Signalr that an invoice has been paid
        /// </summary>
        /// <param name="invoice"></param>
        private static void NotifyClientsInvoicePaid(Invoice invoice)
        {
            var  context  = GlobalHost.ConnectionManager.GetHubContext <NotificationHub>();
            bool isTesnet = GetUseTestnet();

            if (invoice.settle_date == "0" || invoice.settle_date == null)
            {
                // Was not settled
                return;
            }

            //Save in db
            using (CoinpanicContext db = new CoinpanicContext())
            {
                var jar = db.LnCommunityJars.Where(j => j.IsTestnet == isTesnet).First();

                //check if unsettled transaction exists
                var      tx         = db.LnTransactions.Where(tr => tr.PaymentRequest == invoice.payment_request).ToList();
                DateTime settletime = DateTime.UtcNow;

                LnTransaction t;
                if (tx.Count > 0)
                {
                    t = tx.First();
                    t.TimestampSettled = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc) + TimeSpan.FromSeconds(Convert.ToInt64(invoice.settle_date));
                    t.IsSettled        = invoice.settled;
                }
                else
                {
                    //insert transaction
                    settletime = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc) + TimeSpan.FromSeconds(Convert.ToInt64(invoice.settle_date));
                    t          = new LnTransaction()
                    {
                        IsSettled        = invoice.settled,
                        Memo             = invoice.memo,
                        Value            = Convert.ToInt64(invoice.value),
                        IsTestnet        = GetUseTestnet(),
                        HashStr          = invoice.r_hash,
                        IsDeposit        = true,
                        TimestampSettled = settletime,
                        TimestampCreated = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc) + TimeSpan.FromSeconds(Convert.ToInt64(invoice.creation_date)),
                        PaymentRequest   = invoice.payment_request,
                        UserId           = Guid.NewGuid().ToString(),
                    };
                    db.LnTransactions.Add(t);
                }

                var userId = t.UserId;
                var user   = GetUserFromDb(userId, db, jar,
                                           ip: "" // only used when creating a new user, so set blank for this.
                                           );

                user.TotalDeposited     += Convert.ToInt64(invoice.value);
                user.NumDeposits        += 1;
                user.TimesampLastDeposit = DateTime.UtcNow;

                t.IsSettled = invoice.settled;
                if (t.IsDeposit && t.IsSettled)
                {
                    jar.Balance += Convert.ToInt64(invoice.value);
                }
                jar.Transactions.Add(t);
                db.SaveChanges();

                //re-fetch to get the transaction id
                // Ok, this may not be required.
                //var tnew = db.LnTransactions.AsNoTracking().FirstOrDefault(tr => tr.PaymentRequest == invoice.payment_request && (DateTime)tr.TimestampSettled == settletime);

                //if (tnew != null)
                //    t = tnew;

                // Notify Web clients - this is shown to user

                // Client needs to check that the transaction received is theirs before marking successful.
                var newT = new LnCJTransaction()
                {
                    Timestamp = t.TimestampSettled == null ? DateTime.UtcNow : (DateTime)t.TimestampSettled,
                    Amount    = t.Value,
                    Memo      = t.Memo,
                    Type      = t.IsDeposit ? "Deposit" : "Withdrawal",
                    Id        = t.TransactionId,
                    Fee       = t.FeePaid_Satoshi ?? -1,
                };

                context.Clients.All.NotifyNewTransaction(newT);
                if (invoice.settled)
                {
                    context.Clients.All.NotifyInvoicePaid(invoice.payment_request);
                }
            }
        }
示例#3
0
        public ActionResult GetJarDepositInvoice(string amount, string memo)
        {
            string ip = GetClientIpAddress(Request);;

            if (memo == null || memo == "")
            {
                memo = "Coinpanic Community Jar";
            }

            bool useTestnet = GetUseTestnet();
            var  lndClient  = new LndRpcClient(
                host: System.Configuration.ConfigurationManager.AppSettings[useTestnet ? "LnTestnetHost" : "LnMainnetHost"],
                macaroonAdmin: System.Configuration.ConfigurationManager.AppSettings[useTestnet ? "LnTestnetMacaroonAdmin" : "LnMainnetMacaroonAdmin"],
                macaroonRead: System.Configuration.ConfigurationManager.AppSettings[useTestnet ? "LnTestnetMacaroonRead" : "LnMainnetMacaroonRead"],
                macaroonInvoice: System.Configuration.ConfigurationManager.AppSettings[useTestnet ? "LnTestnetMacaroonInvoice" : "LnMainnetMacaroonInvoice"]);

            var inv = lndClient.AddInvoice(Convert.ToInt64(amount), memo: memo, expiry: "432000");

            LnRequestInvoiceResponse resp = new LnRequestInvoiceResponse()
            {
                Invoice = inv.payment_request,
                Result  = "success",
            };

            string userId = "";

            //Check if user is returning
            if (HttpContext.Request.Cookies["CoinpanicCommunityJarUser"] != null)
            {
                var cookie = HttpContext.Request.Cookies.Get("CoinpanicCommunityJarUser");
                cookie.Expires = DateTime.Now.AddDays(7);   //update
                HttpContext.Response.Cookies.Remove("CoinpanicCommunityJarUser");
                HttpContext.Response.SetCookie(cookie);
                userId = cookie.Value;
            }
            else
            {
                HttpCookie cookie = new HttpCookie("CoinpanicCommunityJarUser");
                cookie.Value   = Guid.NewGuid().ToString();
                cookie.Expires = DateTime.Now.AddDays(7);
                HttpContext.Response.Cookies.Remove("CoinpanicCommunityJarUser");
                HttpContext.Response.SetCookie(cookie);
                userId = cookie.Value;
            }

            //Create transaction record (not settled)
            using (CoinpanicContext db = new CoinpanicContext())
            {
                var jar = db.LnCommunityJars.Where(j => j.IsTestnet == useTestnet).First();

                //is this a previous user?
                LnCJUser user;
                user = GetUserFromDb(userId, db, jar, ip);

                //create a new transaction
                LnTransaction t = new LnTransaction()
                {
                    UserId    = user.LnCJUserId,
                    IsSettled = false,
                    Memo      = memo,
                    Value     = Convert.ToInt64(amount),
                    IsTestnet = GetUseTestnet(),
                    HashStr   = inv.r_hash,
                    IsDeposit = true,
                    //TimestampSettled = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc) + TimeSpan.FromSeconds(Convert.ToInt64(invoice.settle_date)),
                    TimestampCreated  = DateTime.Now,
                    PaymentRequest    = inv.payment_request,
                    DestinationPubKey = System.Configuration.ConfigurationManager.AppSettings["LnPubkey"],
                };
                db.LnTransactions.Add(t);
                db.SaveChanges();
            }

            // If a listener is not already running, this should start

            // Check if there is one already online.
            var numListeners = lndTransactionListeners.Count(kvp => kvp.Value.IsLive);

            // If we don't have one running - start it and subscribe
            if (numListeners < 1)
            {
                var listener = lndClient.GetListener();
                lndTransactionListeners.TryAdd(listener.ListenerId, listener); //keep alive while we wait for payment
                listener.InvoicePaid += NotifyClientsInvoicePaid;              //handle payment message
                listener.StreamLost  += OnListenerLost;                        //stream lost
                var a = new Task(() => listener.Start());                      //listen for payment
                a.Start();
            }
            return(Json(resp));
        }
示例#4
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="request"></param>
        /// <param name="userId"></param>
        /// <param name="lndClient"></param>
        /// <returns></returns>
        public object TryWithdrawal(string request, string userId, string ip, LndRpcClient lndClient)
        {
            int maxWithdraw           = 150;
            int maxWithdraw_firstuser = 150;

            if (lndClient == null)
            {
                throw new ArgumentNullException(nameof(lndClient));
            }

            // Lock all threading
            lock (withdrawLock)
            {
                LnCJUser user;
                try
                {
                    var decoded = lndClient.DecodePayment(request);

                    // Check if payment request is ok
                    if (decoded.destination == null)
                    {
                        return(new { Result = "Error decoding invoice." });
                    }

                    // Check that there are funds in the Jar
                    Int64          balance;
                    LnCommunityJar jar;
                    using (CoinpanicContext db = new CoinpanicContext())
                    {
                        jar = db.LnCommunityJars
                              .Where(j => j.IsTestnet == false)
                              .AsNoTracking().First();

                        balance = jar.Balance;

                        if (Convert.ToInt64(decoded.num_satoshis) > balance)
                        {
                            return(new { Result = "Requested amount is greater than the available balance." });
                        }

                        //Get user
                        user = GetUserFromDb(userId, db, jar, ip);

                        var userMax = (user.TotalDeposited - user.TotalWithdrawn);
                        if (userMax < maxWithdraw)
                        {
                            userMax = maxWithdraw;
                        }

                        if (Convert.ToInt64(decoded.num_satoshis) > userMax)
                        {
                            return(new { Result = "Requested amount is greater than maximum allowed." });
                        }
                    }

                    // Check for banned nodes
                    if (IsNodeBanned(decoded.destination, out string banmessage))
                    {
                        return(new { Result = "Banned.  Reason: " + banmessage });
                    }

                    if (decoded.destination == "03a9d79bcfab7feb0f24c3cd61a57f0f00de2225b6d31bce0bc4564efa3b1b5aaf")
                    {
                        return(new { Result = "Can not deposit from jar!" });
                    }

                    //Check rate limits

                    if (nodeWithdrawAttemptTimes.TryGetValue(decoded.destination, out DateTime lastWithdraw))
                    {
                        if ((DateTime.UtcNow - lastWithdraw) < withdrawRateLimit)
                        {
                            return(new { Result = "Rate limit exceeded." });
                        }
                    }

                    bool isanon = false;
                    using (CoinpanicContext db = new CoinpanicContext())
                    {
                        //check if new user
                        DateTime?LastWithdraw = user.TimesampLastWithdraw;

                        //LastWithdraw = db.LnTransactions.Where(tx => tx.IsDeposit == false && tx.IsSettled == true && tx.UserId == user.LnCJUserId).OrderBy(tx => tx.TimestampCreated).AsNoTracking().First().TimestampCreated;
                        if (user.NumWithdraws == 0 && user.NumDeposits == 0)
                        {
                            maxWithdraw = maxWithdraw_firstuser;
                            isanon      = true;
                            //check ip (if someone is not using cookies to rob the site)
                            var userIPs = db.LnCommunityJarUsers.Where(u => u.UserIP == ip).ToList();
                            if (userIPs.Count > 1)
                            {
                                //most recent withdraw
                                LastWithdraw = userIPs.Max(u => u.TimesampLastWithdraw);
                            }

                            // Re-check limits
                            if (Convert.ToInt64(decoded.num_satoshis) > maxWithdraw)
                            {
                                return(new { Result = "Requested amount is greater than maximum allowed for first time users (" + Convert.ToString(maxWithdraw) + ").  Make a deposit." });
                            }
                        }

                        if (user.TotalDeposited - user.TotalWithdrawn < maxWithdraw)
                        {
                            //check for time rate limiting
                            if (DateTime.UtcNow - LastWithdraw < TimeSpan.FromHours(1))
                            {
                                return(new { Result = "You must wait another " + ((user.TimesampLastWithdraw + TimeSpan.FromHours(1)) - DateTime.UtcNow).Value.TotalMinutes.ToString("0.0") + " minutes before withdrawing again, or make a deposit first." });
                            }
                        }

                        //Check if already paid
                        if (db.LnTransactions.Where(tx => tx.PaymentRequest == request && tx.IsSettled).Count() > 0)
                        {
                            return(new { Result = "Invoice has already been paid." });
                        }

                        if (isanon && DateTime.Now - timeLastAnonWithdraw < TimeSpan.FromMinutes(60))
                        {
                            return(new { Result = "Too many first-time user withdraws.  You must wait another " + ((timeLastAnonWithdraw + TimeSpan.FromMinutes(60)) - DateTime.Now).TotalMinutes.ToString("0.0") + " minutes before withdrawing again, or make a deposit first." });
                        }
                    }

                    SendPaymentResponse paymentresult;
                    if (WithdrawRequests.TryAdd(request, DateTime.UtcNow))
                    {
                        paymentresult = lndClient.PayInvoice(request);
                    }
                    else
                    {
                        //double request!
                        Thread.Sleep(1000);

                        //Check if paid (in another thread)
                        using (CoinpanicContext db = new CoinpanicContext())
                        {
                            var txs = db.LnTransactions.Where(t => t.PaymentRequest == request && t.IsSettled).OrderByDescending(t => t.TimestampSettled).AsNoTracking();
                            if (txs.Count() > 0)
                            {
                                //var tx = txs.First();
                                WithdrawRequests.TryRemove(request, out DateTime reqInitTimeA);
                                return(new { Result = "success", Fees = "0" });
                            }

                            return(new { Result = "Please click only once.  Payment already in processing." });
                        }
                    }
                    WithdrawRequests.TryRemove(request, out DateTime reqInitTime);

                    if (paymentresult.payment_error != null)
                    {
                        // Save payment error to database
                        using (CoinpanicContext db = new CoinpanicContext())
                        {
                            user = GetUserFromDb(userId, db, jar, ip);
                            LnTransaction t = new LnTransaction()
                            {
                                UserId            = user.LnCJUserId,
                                IsSettled         = false,
                                Memo              = decoded.description ?? "Withdraw",
                                Value             = Convert.ToInt64(decoded.num_satoshis),
                                IsTestnet         = false,
                                HashStr           = decoded.payment_hash,
                                IsDeposit         = false,
                                TimestampCreated  = DateTime.UtcNow, //can't know
                                PaymentRequest    = request,
                                DestinationPubKey = decoded.destination,
                                IsError           = true,
                                ErrorMessage      = paymentresult.payment_error,
                            };
                            db.LnTransactions.Add(t);
                            db.SaveChanges();
                        }
                        return(new { Result = "Payment Error: " + paymentresult.payment_error });
                    }

                    // We have a successful payment

                    // Record time of withdraw to the node
                    nodeWithdrawAttemptTimes.TryAdd(decoded.destination, DateTime.UtcNow);

                    // Notify client(s)
                    var context = GlobalHost.ConnectionManager.GetHubContext <NotificationHub>();

                    using (CoinpanicContext db = new CoinpanicContext())
                    {
                        user = GetUserFromDb(userId, db, jar, ip);
                        user.NumWithdraws        += 1;
                        user.TotalWithdrawn      += Convert.ToInt64(decoded.num_satoshis);
                        user.TimesampLastWithdraw = DateTime.UtcNow;

                        //insert transaction
                        LnTransaction t = new LnTransaction()
                        {
                            UserId            = user.LnCJUserId,
                            IsSettled         = true,
                            Memo              = decoded.description == null ? "Withdraw" : decoded.description,
                            Value             = Convert.ToInt64(decoded.num_satoshis),
                            IsTestnet         = false,
                            HashStr           = decoded.payment_hash,
                            IsDeposit         = false,
                            TimestampSettled  = DateTime.UtcNow,
                            TimestampCreated  = DateTime.UtcNow, //can't know
                            PaymentRequest    = request,
                            FeePaid_Satoshi   = (paymentresult.payment_route.total_fees == null ? 0 : Convert.ToInt64(paymentresult.payment_route.total_fees)),
                            NumberOfHops      = paymentresult.payment_route.hops == null ? 0 : paymentresult.payment_route.hops.Count(),
                            DestinationPubKey = decoded.destination,
                        };
                        db.LnTransactions.Add(t);
                        db.SaveChanges();

                        jar          = db.LnCommunityJars.Where(j => j.IsTestnet == false).First();
                        jar.Balance -= Convert.ToInt64(decoded.num_satoshis);
                        jar.Balance -= paymentresult.payment_route.total_fees != null?Convert.ToInt64(paymentresult.payment_route.total_fees) : 0;

                        jar.Transactions.Add(t);
                        db.SaveChanges();

                        var newT = new LnCJTransaction()
                        {
                            Timestamp = t.TimestampSettled == null ? DateTime.UtcNow : (DateTime)t.TimestampSettled,
                            Amount    = t.Value,
                            Memo      = t.Memo,
                            Type      = t.IsDeposit ? "Deposit" : "Withdrawal",
                            Id        = t.TransactionId,
                        };

                        context.Clients.All.NotifyNewTransaction(newT);
                    }

                    if (isanon)
                    {
                        timeLastAnonWithdraw = DateTime.Now;
                    }

                    return(new { Result = "success", Fees = (paymentresult.payment_route.total_fees == null ? "0" : paymentresult.payment_route.total_fees) });
                }
                catch (Exception e)
                {
                    return(new { Result = "Error decoding request." });
                }
            }
            return(new { Result = "Error decoding request." });
        }