private string FormatJson(HotBitcoinAddresses addresses)
        {
            StringBuilder result = new StringBuilder(16384);

            Dictionary <BitcoinChain, double> conversionRateLookup = new Dictionary <BitcoinChain, double>();
            Dictionary <BitcoinChain, Int64>  satoshisTotalLookup  = new Dictionary <BitcoinChain, long>();

            long fiatCentsPerCoreCoin = new Money(BitcoinUtility.SatoshisPerBitcoin, Currency.BitcoinCore).ToCurrency(_authenticationData.CurrentOrganization.Currency).Cents;
            long fiatCentsPerCashCoin = new Money(BitcoinUtility.SatoshisPerBitcoin, Currency.BitcoinCash).ToCurrency(_authenticationData.CurrentOrganization.Currency).Cents;

            conversionRateLookup[BitcoinChain.Cash] = fiatCentsPerCashCoin / 1.0 / BitcoinUtility.SatoshisPerBitcoin;   // the "/1.0" converts to double implicitly
            conversionRateLookup[BitcoinChain.Core] = fiatCentsPerCoreCoin / 1.0 / BitcoinUtility.SatoshisPerBitcoin;

            result.Append("{\"rows\":[");

            int addressesWithFunds = 0;

            foreach (HotBitcoinAddress address in addresses)
            {
                HotBitcoinAddressUnspents unspents = HotBitcoinAddressUnspents.ForAddress(address);
                Int64 satoshisUnspentAddress       = 0;

                StringBuilder childResult = new StringBuilder(16384);
                foreach (HotBitcoinAddressUnspent unspent in unspents)
                {
                    childResult.Append("{");
                    childResult.AppendFormat(
                        "\"id\":\"UTXO{0}\"," +
                        "\"derivePath\":\"{1}\"," +
                        "\"address\":\"{2}\"," +
                        "\"balanceMicrocoins\":\"{3}\"," +
                        "\"balanceFiat\":\"{4}\"",
                        unspent.Identity,
                        Resources.Pages.Ledgers.BitcoinHotWallet_UnspentTransaction,
                        unspent.TransactionHash,
                        (unspent.AmountSatoshis / 100.0).ToString("N2"),
                        (unspent.AmountSatoshis / 100.0 * conversionRateLookup[unspent.Address.Chain]).ToString("N2")
                        );
                    satoshisUnspentAddress += unspent.AmountSatoshis;
                    childResult.Append("},");
                }
                if (unspents.Count > 0)
                {
                    childResult.Remove(childResult.Length - 1, 1);  // remove last comma
                }

                if (satoshisUnspentAddress > 0)
                {
                    result.Append("{");
                    result.AppendFormat(
                        "\"id\":\"{0}\"," +
                        "\"derivePath\":\"{1}\"," +
                        "\"address\":\"{2}\"," +
                        "\"balanceMicrocoins\":\"{3}\"," +
                        "\"balanceFiat\":\"{4}\",",
                        address.Identity,
                        address.Chain.ToString() + " " + address.DerivationPath,
                        (address.Chain == BitcoinChain.Cash? address.HumanAddress.Substring("bitcoincash:".Length): address.ProtocolLevelAddress),
                        JsonExpandingString(address.Identity, satoshisUnspentAddress),
                        JsonExpandingString(address.Identity, (Int64)(satoshisUnspentAddress * conversionRateLookup[address.Chain]))
                        );
                    result.Append("\"state\":\"closed\",\"children\":[" + childResult.ToString() + "]");
                    result.Append("},");
                    addressesWithFunds++;

                    if (!satoshisTotalLookup.ContainsKey(address.Chain))
                    {
                        satoshisTotalLookup[address.Chain] = 0;
                    }

                    satoshisTotalLookup[address.Chain] += satoshisUnspentAddress;
                }
            }

            if (addressesWithFunds > 0)
            {
                result.Remove(result.Length - 1, 1); // remove last comma
            }
            else // no funds at all in hotwallet
            {
                result.Append("{");
                result.AppendFormat(
                    "\"id\":\"0\"," +
                    "\"derivePath\":\"0\"," +
                    "\"address\":\"{0}\"," +
                    "\"balanceMicrocoins\":\"{1}\"," +
                    "\"balanceFiat\":\"{2}\"",
                    Resources.Pages.Ledgers.BitcoinHotWallet_Empty,
                    0.0.ToString("N2"),
                    0.0.ToString("N2")
                    );
                result.Append("}");
            }

            result.Append("]");

            if (satoshisTotalLookup.Count > 0)
            {
                // We should also have a footer, because we need a total

                result.Append(",\"footer\":[");

                bool previousFooterRow = false;

                foreach (BitcoinChain chain in satoshisTotalLookup.Keys)
                {
                    if (previousFooterRow)
                    {
                        result.Append(",");
                    }

                    result.Append("{");

                    result.AppendFormat(
                        "\"derivePath\":\"" + Resources.Global.Global_Total.ToUpperInvariant() + " " +
                        (string)chain.ToString().ToUpperInvariant() + "\",\"balanceMicrocoins\":\"{0}\",\"balanceFiat\":\"{1}\"",
                        (satoshisTotalLookup[chain] / 100.0).ToString("N2"), (satoshisTotalLookup[chain] / 100.0 * conversionRateLookup[chain]).ToString("N2"));

                    result.Append("}"); // on separate line to suppress warning*/

                    previousFooterRow = true;
                }

                result.Append("]");
            }

            result.Append("}");

            return(result.ToString());
        }
Exemplo n.º 2
0
        static public AjaxCallResult ProcessTransactionReceived(string guid, string txHash)
        {
            AuthenticationData authData = GetAuthenticationDataAndCulture(); // just to make sure we're called properly
            BitcoinChain       chain    = BitcoinChain.Cash;

            string bitcoinAddress = (string)GuidCache.Get(guid);

            if (BitcoinUtility.TestUnspents(chain, bitcoinAddress))
            {
                HotBitcoinAddressUnspents unspents = HotBitcoinAddress.FromAddress(chain, bitcoinAddress).Unspents;
                Int64 satoshisReceived             = unspents.Last().AmountSatoshis;

                if (unspents.Last().TransactionHash != txHash && txHash.Length > 0)
                {
                    // Race condition.
                    Debugger.Break();
                }

                HotBitcoinAddressUnspent utxoToReturn = unspents.Last();

                Swarmops.Logic.Financial.Money moneyReceived = new Swarmops.Logic.Financial.Money(satoshisReceived,
                                                                                                  Currency.BitcoinCash);

                // Create success message and ledger transaction
                string successMessage = string.Empty;

                // TODO: Get the tx, get the input

                string returnAddress = BitcoinUtility.GetInputAddressesForTransaction(chain, txHash) [0]; // assumes at least one input address

                // Return the money, too. Set fee for a 300-byte transaction.

                ReturnBitcoinEchoUtxoOrder backendOrder = new ReturnBitcoinEchoUtxoOrder(utxoToReturn);
                backendOrder.Create(authData.CurrentOrganization, authData.CurrentUser);

                string tx1Description = "Bitcoin technical echo test (will be repaid immediately)";


                if (authData.CurrentOrganization.Currency.IsBitcoinCash)
                {
                    // The ledger is native bitcoin, so cent units are satoshis

                    FinancialTransaction ledgerTx1 = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                 DateTime.UtcNow, tx1Description);
                    ledgerTx1.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsOther, -(satoshisReceived), authData.CurrentUser);
                    ledgerTx1.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, satoshisReceived, authData.CurrentUser);
                    ledgerTx1.BlockchainHash = txHash;

                    // The return payment will be logged when made, so its hash can be recorded

                    if (satoshisReceived % 100 == 0)
                    {
                        successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N0"));
                    }
                    else
                    {
                        successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N2"));
                    }
                }
                else
                {
                    // The ledger is NOT native bitcoin, so we'll need to convert currencies

                    long orgNativeCents            = moneyReceived.ToCurrency(authData.CurrentOrganization.Currency).Cents;
                    FinancialTransaction ledgerTx1 = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                 DateTime.UtcNow, tx1Description);
                    ledgerTx1.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsOther, -orgNativeCents, authData.CurrentUser);
                    ledgerTx1.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, orgNativeCents, authData.CurrentUser).AmountForeignCents = new Swarmops.Logic.Financial.Money(satoshisReceived, Currency.BitcoinCash);
                    ledgerTx1.BlockchainHash = txHash;

                    // The second transaction is logged when executed in the back-end order

                    successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceived,
                                                   authData.CurrentOrganization.Currency.DisplayCode, orgNativeCents / 100.0, satoshisReceived / 100.0);
                }

                return(new AjaxCallResult()
                {
                    DisplayMessage = successMessage, Success = true
                });

                // TODO: Ack donation via mail?
                // TODO: Notify CFO/etc of donation?
            }

            return(new AjaxCallResult()
            {
                Success = false
            });
        }
Exemplo n.º 3
0
        static public AjaxCallResult ProcessTransactionReceived(string guid, string txHash)
        {
            BitcoinChain chain = BitcoinChain.Cash;

            AuthenticationData authData = GetAuthenticationDataAndCulture(); // just to make sure we're called properly

            string bitcoinAddress = (string)GuidCache.Get(guid);

            if (BitcoinUtility.TestUnspents(chain, bitcoinAddress))
            {
                HotBitcoinAddressUnspents unspents = HotBitcoinAddress.FromAddress(chain, bitcoinAddress).Unspents;

                // TODO: Update the HotBitcoinAddress with the new amount?

                HotBitcoinAddressUnspent unspent = null;
                Int64 satoshisReceived           = 0;

                foreach (HotBitcoinAddressUnspent potentialUnspent in unspents)
                {
                    if (potentialUnspent.TransactionHash == txHash)
                    {
                        satoshisReceived = potentialUnspent.AmountSatoshis;
                        unspent          = potentialUnspent;
                    }
                }

                if (unspent == null)  // Supplied transaction hash was not found in collection
                {
                    Debugger.Break(); // TODO: Something else than break the debugger
                }

                Swarmops.Logic.Financial.Money moneyReceived = new Swarmops.Logic.Financial.Money(satoshisReceived,
                                                                                                  Currency.BitcoinCash);

                // Make sure that the hotwallet native currency is bitcoin cash
                authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot.ForeignCurrency = Currency.BitcoinCash;

                // Create success message and ledger transaction
                string successMessage = string.Empty;

                FinancialTransaction testTransaction = null;
                try
                {
                    testTransaction = FinancialTransaction.FromBlockchainHash(authData.CurrentOrganization, txHash);

                    // We've already seen this donation! Something is seriously bogus here
                    Debugger.Break();
                    return(new AjaxCallResult()
                    {
                        DisplayMessage = successMessage, Success = true
                    });
                }
                catch (ArgumentException)
                {
                    // This exception is expected - the transaction should not yet exist
                }

                if (authData.CurrentOrganization.Currency.IsBitcoinCash)
                {
                    // The ledger is native bitcoin cash, so units are Satoshis

                    FinancialTransaction ledgerTx = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                DateTime.UtcNow, "Donation (bitcoin to hotwallet)");
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.IncomeDonations, -satoshisReceived, authData.CurrentUser);
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, satoshisReceived, authData.CurrentUser);
                    ledgerTx.BlockchainHash = txHash;

                    if (satoshisReceived % 100 == 0)
                    {
                        successMessage = string.Format(Resources.Pages.Financial.Donate_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N0"));
                    }
                    else
                    {
                        successMessage = string.Format(Resources.Pages.Financial.Donate_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N2"));
                    }
                }
                else
                {
                    // The ledger is NOT native bitcoin, so we'll need to convert currencies

                    long orgNativeCents           = moneyReceived.ToCurrency(authData.CurrentOrganization.Currency).Cents;
                    FinancialTransaction ledgerTx = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                DateTime.UtcNow, "Donation (bitcoin to hotwallet)");
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.IncomeDonations, -orgNativeCents, authData.CurrentUser);
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, orgNativeCents, authData.CurrentUser).AmountForeignCents = new Swarmops.Logic.Financial.Money(satoshisReceived, Currency.BitcoinCash);
                    ledgerTx.BlockchainHash = txHash;

                    successMessage = string.Format(Resources.Pages.Financial.Donate_FundsReceived,
                                                   authData.CurrentOrganization.Currency.DisplayCode, orgNativeCents / 100.0, satoshisReceived / 100.0);
                }

                return(new AjaxCallResult()
                {
                    DisplayMessage = successMessage, Success = true
                });

                // TODO: Ack donation via mail?
                // TODO: Notify CFO/etc of donation?
            }

            return(new AjaxCallResult()
            {
                Success = false
            });
        }
        public override void Run()
        {
            HotBitcoinAddressUnspent utxoToReturn = HotBitcoinAddressUnspent.FromIdentity(UtxoIdentity);
            HotBitcoinAddress        utxoAddress  = utxoToReturn.Address;
            BitcoinSecret            secretKey    = utxoAddress.PrivateKey;

            // TODO: Verify that the utxoAddress is an EchoTest address, i.e. has second path component == BitcoinUtility.BitcoinEchoTestIndex

            string returnAddress = BitcoinUtility.GetInputAddressesForTransaction(BitcoinChain.Cash, utxoToReturn.TransactionHash)[0]; // assumes at least one input address -- not coinbase

            // Return the money

            BitcoinTransactionInputs inputs = utxoToReturn.AsInputs;
            Int64 satoshisToReturn          = utxoToReturn.AmountSatoshis;

            Coin[]    coins       = inputs.Coins;
            ICoin[]   iCoins      = coins;
            ISecret[] privateKeys = utxoToReturn.AsInputs.PrivateKeys;

            TransactionBuilder txBuilder = new TransactionBuilder();

            txBuilder = txBuilder.SendFees(new Satoshis(BitcoinUtility.EchoFeeSatoshis));
            txBuilder = txBuilder.AddCoins(iCoins);
            txBuilder = txBuilder.AddKeys(privateKeys);

            if (returnAddress.StartsWith("1"))
            {
                txBuilder = txBuilder.Send(new BitcoinPubKeyAddress(returnAddress),
                                           new Satoshis(utxoToReturn.AmountSatoshis - BitcoinUtility.EchoFeeSatoshis));
            }
            else if (returnAddress.StartsWith("3"))
            {
                txBuilder = txBuilder.Send(new BitcoinScriptAddress(returnAddress, Network.Main),
                                           new Satoshis(utxoToReturn.AmountSatoshis - BitcoinUtility.EchoFeeSatoshis));
            }
            else
            {
                throw new ArgumentException("Unrecognized address format");
            }

            Transaction tx = txBuilder.BuildTransaction(true, SigHash.ForkId | SigHash.All);

            BitcoinUtility.BroadcastTransaction(tx, BitcoinChain.Cash);
            utxoToReturn.Delete();
            utxoAddress.UpdateTotal();

            // Update the ledger

            string tx2Description          = "Bitcoin echo test repayment";
            FinancialTransaction ledgerTx2 = FinancialTransaction.Create(this.Organization,
                                                                         DateTime.UtcNow, tx2Description);

            if (this.Organization.Currency.IsBitcoinCash)
            {
                ledgerTx2.AddRow(this.Organization.FinancialAccounts.DebtsOther, satoshisToReturn, this.Person);
                ledgerTx2.AddRow(this.Organization.FinancialAccounts.AssetsBitcoinHot, -satoshisToReturn, this.Person);
            }
            else
            {
                Int64 centsPresentation =
                    new Swarmops.Logic.Financial.Money(satoshisToReturn, Currency.BitcoinCash).ToCurrency(this.Organization.Currency).Cents;
                ledgerTx2.AddRow(this.Organization.FinancialAccounts.DebtsOther, centsPresentation, this.Person);
                ledgerTx2.AddRow(this.Organization.FinancialAccounts.AssetsBitcoinHot, -centsPresentation, this.Person);
            }
            ledgerTx2.BlockchainHash = tx.GetHash().ToString();
        }
Exemplo n.º 5
0
        static public AjaxCallResult CheckTransactionReceived(string guid, string txHash)
        {
            AuthenticationData authData = GetAuthenticationDataAndCulture(); // just to make sure we're called properly

            string bitcoinAddress = (string)GuidCache.Get(guid);

            if (BitcoinUtility.TestUnspents(bitcoinAddress))
            {
                HotBitcoinAddressUnspents unspents = HotBitcoinAddress.FromAddress(bitcoinAddress).Unspents;

                // TODO: Update the HotBitcoinAddress with the new amount?

                Int64 satoshisReceived = unspents.Last().AmountSatoshis;

                if (unspents.Last().TransactionHash != txHash && txHash.Length > 0)
                {
                    // Race condition.
                    Debugger.Break();
                }

                Swarmops.Logic.Financial.Money moneyReceived = new Swarmops.Logic.Financial.Money(satoshisReceived,
                                                                                                  Currency.Bitcoin);

                // Make sure that the hotwallet native currency is bitcoin
                authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot.ForeignCurrency = Currency.Bitcoin;

                // Create success message and ledger transaction
                string successMessage = string.Empty;

                // TODO: Get the tx, get the input

                string returnAddress = BitcoinUtility.GetInputAddressesForTransaction(txHash) [0]; // assumes at least one input address

                if (authData.CurrentOrganization.Currency.IsBitcoin)
                {
                    // The ledger is native bitcoin, so units are Satoshis

                    FinancialTransaction ledgerTx = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                DateTime.UtcNow, "Bitcoin echo test (will be repaid immediately)");
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsOther, -satoshisReceived, authData.CurrentUser);
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, satoshisReceived, authData.CurrentUser);
                    ledgerTx.BlockchainHash = txHash;

                    if (satoshisReceived % 100 == 0)
                    {
                        successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N0"));
                    }
                    else
                    {
                        successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceivedNative,
                                                       (satoshisReceived / 100.0).ToString("N2"));
                    }

                    // TODO: Log the payout back, as an inbound invoice for immediate payout
                }
                else
                {
                    // The ledger is NOT native bitcoin, so we'll need to convert currencies

                    long orgNativeCents           = moneyReceived.ToCurrency(authData.CurrentOrganization.Currency).Cents;
                    FinancialTransaction ledgerTx = FinancialTransaction.Create(authData.CurrentOrganization,
                                                                                DateTime.UtcNow, "Bitcoin echo test (will be repaid immediately)");
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.DebtsOther, -orgNativeCents, authData.CurrentUser);
                    ledgerTx.AddRow(authData.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot, orgNativeCents, authData.CurrentUser).AmountForeignCents = new Swarmops.Logic.Financial.Money(satoshisReceived, Currency.Bitcoin);
                    ledgerTx.BlockchainHash = txHash;

                    successMessage = string.Format(Resources.Pages.Admin.BitcoinEchoTest_FundsReceived,
                                                   authData.CurrentOrganization.Currency.DisplayCode, orgNativeCents / 100.0, satoshisReceived / 100.0);

                    // TODO: Create a payout back for this amount -- needs to be specified in bitcoin -- as an inbound invoice
                }

                return(new AjaxCallResult()
                {
                    DisplayMessage = successMessage, Success = true
                });

                // TODO: Ack donation via mail?
                // TODO: Notify CFO/etc of donation?
            }

            return(new AjaxCallResult()
            {
                Success = false
            });
        }