protected void Page_Load(object sender, EventArgs e)
        {
            PageAccessRequired       = new Access(CurrentOrganization, AccessAspect.BookkeepingDetails, AccessType.Read);
            this._authenticationData = GetAuthenticationDataAndCulture();

            HotBitcoinAddresses addresses = HotBitcoinAddresses.ForOrganization(_authenticationData.CurrentOrganization);

            foreach (HotBitcoinAddress address in addresses)
            {
                if (address.Chain == BitcoinChain.Core)
                {
                    // These shouldn't exist much, so make sure that we have the equivalent Cash address registered
                    try
                    {
                        HotBitcoinAddress.FromAddress(BitcoinChain.Cash, address.ProtocolLevelAddress);
                    }
                    catch (ArgumentException)
                    {
                        // We didn't have it, so create it
                        BitcoinUtility.TestUnspents(BitcoinChain.Cash, address.ProtocolLevelAddress);
                    }
                }
            }

            Response.ContentType = "application/json";
            Response.Output.WriteLine(FormatJson(addresses));
            Response.End();
        }
示例#2
0
        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);

            ledgerTx2.AddRow(this.Organization.FinancialAccounts.DebtsOther, satoshisToReturn, this.Person);
            ledgerTx2.AddRow(this.Organization.FinancialAccounts.AssetsBitcoinHot, -satoshisToReturn, this.Person);
            ledgerTx2.BlockchainHash = tx.GetHash().ToString();
        }
示例#3
0
        protected void Page_Load(object sender, EventArgs e)
        {
            this.PageAccessRequired = new Access(this.CurrentOrganization, AccessAspect.Administration);

            // This page is copied from Donate, with the echo payout added to the end

            this.PageTitle                = Resources.Pages.Admin.BitcoinEchoTest_PageTitle;
            this.InfoBoxLiteral           = String.Format(Resources.Pages.Admin.BitcoinEchoTest_Info, BitcoinUtility.EchoFeeSatoshis / 100.0);
            this.LabelStatus.Text         = Resources.Pages.Admin.BitcoinEchoTest_StatusInitial;
            this.SuppressStatisticHeaders = true;

            if (this.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot == null)
            {
                this.PanelDisabled.Visible = true;
                this.PanelEnabled.Visible  = false;
            }

            DateTime utcNow = DateTime.UtcNow;

            this._currency = CurrentOrganization.Currency;

            HotBitcoinAddress address = HotBitcoinAddress.CreateUnique(this.CurrentOrganization, BitcoinChain.Cash,
                                                                       BitcoinUtility.BitcoinEchoTestIndex, this.CurrentUser.Identity, utcNow.Year, utcNow.Month, utcNow.Day);

            this.BitcoinCashAddressLegacy = address.ProtocolLevelAddress;
            this.BitcoinCashAddressCash   = address.HumanAddress.Substring("bitcoincash:".Length);
            string guid = Guid.NewGuid().ToString("N");

            GuidCache.Set(guid, address.ProtocolLevelAddress);
            this.TransactionGuid = guid;

            // Calculate conversion rate (satoshi-cents to unit-cents, so we're good, even if the conversion rate
            // is calculated on microbitcoin to whole units)

            this.ConversionRateSatoshisToCents = Currency.BitcoinCash.GetConversionRate(CurrentOrganization.Currency);

            // BEGIN TEST CODE

            // END TEST CODE

            this.BoxTitle.Text = Resources.Pages.Admin.BitcoinEchoTest_PageTitle;
            this.LabelExplainBitcoinEchoTest.Text = String.Format(Resources.Pages.Admin.BitcoinEchoTest_Explain,
                                                                  CurrentOrganization.Name, address.HumanAddress, BitcoinUtility.EchoFeeSatoshis / 100.0);

            this.ImageBitcoinQr.ImageUrl =
                "https://chart.googleapis.com/chart?cht=qr&chs=400x400&chl=" +
                HttpUtility.UrlEncode(address.HumanAddress + "?label=" +
                                      Uri.EscapeDataString("Bitcoin Echo Test"));   // URI scheme doesn't like &, =
        }
示例#4
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
            });
        }
示例#5
0
        internal static void ProcessBitcoinTransaction(JObject blockchainTransaction)
        {
            /* Format:
             *
             *  {
             *      "op": "utx",
             *      "x": {
             *          "lock_time": 0,
             *          "ver": 1,
             *          "size": 192,
             *          "inputs": [
             *              {
             *                  "sequence": 4294967295,
             *                  "prev_out": {
             *                      "spent": true,
             *                      "tx_index": 99005468,
             *                      "type": 0,
             *                      "addr": "1BwGf3z7n2fHk6NoVJNkV32qwyAYsMhkWf",
             *                      "value": 65574000,
             *                      "n": 0,
             *                      "script": "76a91477f4c9ee75e449a74c21a4decfb50519cbc245b388ac"
             *                  },
             *                  "script": "483045022100e4ff962c292705f051c2c2fc519fa775a4d8955bce1a3e29884b2785277999ed02200b537ebd22a9f25fbbbcc9113c69c1389400703ef2017d80959ef0f1d685756c012102618e08e0c8fd4c5fe539184a30fe35a2f5fccf7ad62054cad29360d871f8187d"
             *              }
             *          ],
             *          "time": 1440086763,
             *          "tx_index": 99006637,
             *          "vin_sz": 1,
             *          "hash": "0857b9de1884eec314ecf67c040a2657b8e083e1f95e31d0b5ba3d328841fc7f",
             *          "vout_sz": 1,
             *          "relayed_by": "127.0.0.1",
             *          "out": [
             *              {
             *                  "spent": false,
             *                  "tx_index": 99006637,
             *                  "type": 0,
             *                  "addr": "1A828tTnkVFJfSvLCqF42ohZ51ksS3jJgX",
             *                  "value": 65564000,
             *                  "n": 0,
             *                  "script": "76a914640cfdf7b79d94d1c980133e3587bd6053f091f388ac"
             *              }
             *          ]
             *      }
             *  }
             */

            Console.WriteLine(" - transaction received");

            if (_transactionCache == null)
            {
                _transactionCache = new SerializableDictionary <string, JObject>();
            }

            string txHash = (string)blockchainTransaction["x"]["hash"];

            _transactionCache[txHash] = (JObject)blockchainTransaction["x"];

            foreach (JObject outpoint in blockchainTransaction["x"]["out"])
            {
                Satoshis satoshis      = Int64.Parse((string)outpoint["value"]);
                string   addressString = (string)outpoint["addr"];

                HotBitcoinAddress hotAddress = null;

                try
                {
                    hotAddress = HotBitcoinAddress.FromAddress(BitcoinChain.Cash, addressString);
                }
                catch (ArgumentException)
                {
                    // Ignore this - it means the addressString isn't ours
                    continue;
                }

                if (hotAddress != null)
                {
                    JObject json = new JObject();
                    json["MessageType"] = "BitcoinReceived";
                    json["Address"]     = addressString;
                    json["Hash"]        = txHash;

                    Currency currency = hotAddress.Organization.Currency;
                    json["OrganizationId"] = hotAddress.OrganizationId.ToString();
                    json["Currency"]       = currency.Code;
                    Swarmops.Logic.Financial.Money organizationCents = new Money(satoshis, Currency.BitcoinCore).ToCurrency(currency);
                    json["Satoshis"]       = satoshis.ToString();
                    json["Cents"]          = organizationCents.Cents.ToString();
                    json["CentsFormatted"] = String.Format("{0:N2}", organizationCents.Cents / 100.0);

                    _socketServer.WebSocketServices.Broadcast(json.ToString());

                    // TODO: Examine what address this is, handle accordingly
                }
            }
        }
示例#6
0
        protected void Page_Load(object sender, EventArgs e)
        {
            this.PageAccessRequired = new Access(this.CurrentOrganization, AccessAspect.Participant);
            this.IsDashboard        = true; // prevents encaps and enables odometer

            /* TEMP TEMP TEMP - REMOVE THIS CODE */

            /*
             * Organization fwn = Organization.FromIdentity (2);
             *
             * Salaries salaries = Salaries.ForOrganization (fwn);
             * foreach (Salary salary in salaries)
             * {
             *  if (salary.PayrollItem.Person.BitcoinPayoutAddress.Length > 0 && salary.Attested == false)
             *  {
             *      salary.Attest (salary.PayrollItem.Person); // null for system apparently isn't allowed here
             *  }
             * }*/

            this.PageTitle                = Resources.Pages.Financial.Donate_PageTitle;
            this.InfoBoxLiteral           = Resources.Pages.Financial.Donate_Info;
            this.LabelStatus.Text         = Resources.Pages.Financial.Donate_StatusInitial;
            this.SuppressStatisticHeaders = true;

            if (this.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot == null)
            {
                this.PanelDisabled.Visible = true;
                this.PanelEnabled.Visible  = false;
            }

            HotBitcoinAddress address = HotBitcoinAddress.Create(this.CurrentOrganization, BitcoinChain.Cash,
                                                                 BitcoinUtility.BitcoinDonationsIndex, this.CurrentUser.Identity);

            this.BitcoinCashAddressUsed = address.ProtocolLevelAddress;
            string guid = Guid.NewGuid().ToString("N");

            GuidCache.Set(guid, address.ProtocolLevelAddress);
            this.TransactionGuid = guid;

            // Calculate conversion rate (satoshi-cents to unit-cents, so we're good, even if the conversion rate
            // is calculated on microbitcoin to whole units)

            this.ConversionRateSatoshisToCents = Currency.BitcoinCash.GetConversionRate(CurrentOrganization.Currency);

            // Add subscription to address

            /*    --- RETIRED CODE -- THIS WAS NOT RELIABLE -- DONE CLIENT SIDE INSTEAD
             * using (
             *  WebSocket socket =
             *      new WebSocket("ws://localhost:" + SystemSettings.WebsocketPortFrontend + "/Front?Auth=" +
             *                    Uri.EscapeDataString(this.CurrentAuthority.ToEncryptedXml())))
             * {
             *  socket.Connect();
             *
             *  JObject data = new JObject();
             *  data ["ServerRequest"] = "AddBitcoinAddress";
             *  data["Address"] = address.ProtocolLevelAddress;
             *  socket.Send(data.ToString());
             *  socket.Ping(); // wait a little little while for send to work
             *  socket.Close();
             * }*/

            this.BoxTitle.Text = Resources.Pages.Financial.Donate_PageTitle;
            this.LabelExplainBitcoinDonation.Text = String.Format(Resources.Pages.Financial.Donate_Explain,
                                                                  CurrentOrganization.Name, address.ProtocolLevelAddress);
            this.LabelReceivedFunds.Text = String.Format(Resources.Pages.Financial.Donate_FundsReceivedLabel,
                                                         CurrentOrganization.Currency.DisplayCode);

            this.ImageBitcoinQr.ImageUrl =
                "https://chart.googleapis.com/chart?cht=qr&chs=400x400&chl=bitcoincash:" +
                HttpUtility.UrlEncode(address.ProtocolLevelAddress + "?label=" +
                                      Uri.EscapeDataString(String.Format(Resources.Pages.Financial.Donate_TxLabel,
                                                                         CurrentOrganization.Name))); // URI scheme doesn't like &, =
        }
示例#7
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
            });
        }
        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
            });
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            this.PageAccessRequired = new Access(this.CurrentOrganization, AccessAspect.Administration);

            // This page is copied from Donate, with the echo payout added to the end


            /* TEMP TEMP TEMP - REMOVE THIS CODE */

            /*
             * Organization fwn = Organization.FromIdentity (2);
             *
             * Salaries salaries = Salaries.ForOrganization (fwn);
             * foreach (Salary salary in salaries)
             * {
             *  if (salary.PayrollItem.Person.BitcoinPayoutAddress.Length > 0 && salary.Attested == false)
             *  {
             *      salary.Attest (salary.PayrollItem.Person); // null for system apparently isn't allowed here
             *  }
             * }*/

            this.PageTitle              = Resources.Pages.Admin.BitcoinEchoTest_PageTitle;
            this.InfoBoxLiteral         = Resources.Pages.Admin.BitcoinEchoTest_Info;
            this.LabelStatus.Text       = Resources.Pages.Admin.BitcoinEchoTest_StatusInitial;
            this.LiteralTxDetected.Text = JavascriptEscape(Resources.Pages.Admin.BitcoinEchoTest_TransactionDetected);

            if (this.CurrentOrganization.FinancialAccounts.AssetsBitcoinHot == null)
            {
                this.PanelDisabled.Visible = true;
                this.PanelEnabled.Visible  = false;
                this.LiteralEnable.Text    = @"false";
            }

            HotBitcoinAddress address = HotBitcoinAddress.Create(this.CurrentOrganization,
                                                                 BitcoinUtility.BitcoinDonationsIndex, this.CurrentUser.Identity);

            this.LiteralBitcoinAddress.Text = address.Address;
            string guid = Guid.NewGuid().ToString("N");

            GuidCache.Set(guid, address.Address);
            this.LiteralGuid.Text = guid;

            // TEST TEST TEST

            /*
             * BitcoinSecret secretKey = address.PrivateKey;
             * Coin[] spendableCoin = BitcoinUtility.GetSpendableCoin (secretKey);
             *
             * TransactionBuilder txBuild = new TransactionBuilder();
             * Transaction tx = txBuild.AddCoins (spendableCoin)
             *  .AddKeys (secretKey)
             *  .Send (new BitcoinAddress (BitcoinUtility.BitcoinTestAddress), new Satoshis (address.UnspentSatoshis - BitcoinUtility.FeeSatoshisPerThousandBytes))
             *  .SendFees (new Satoshis(BitcoinUtility.FeeSatoshisPerThousandBytes))
             *  .SetChange (secretKey.GetAddress())
             *  .BuildTransaction (true);
             *
             * bool test = txBuild.Verify (tx);
             * if (!test)
             * {
             *  throw new InvalidOperationException("Tx is not properly signed");
             * }
             *
             * BitcoinUtility.BroadcastTransaction (tx);*/

            this.BoxTitle.Text = Resources.Pages.Financial.Donate_PageTitle;
            this.LabelExplainBitcoinDonation.Text = String.Format(Resources.Pages.Financial.Donate_Explain,
                                                                  CurrentOrganization.Name, address.Address);

            this.ImageBitcoinQr.ImageUrl =
                "https://chart.googleapis.com/chart?cht=qr&chs=400x400&chl=bitcoin:" +
                HttpUtility.UrlEncode(address.Address + "?label=" +
                                      Uri.EscapeDataString(String.Format(Resources.Pages.Financial.Donate_TxLabel,
                                                                         CurrentOrganization.Name))); // URI scheme doesn't like &, =
        }