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(); }
public static AjaxCallResult SetBitcoinPayoutAddress(string bitcoinAddress) { AuthenticationData authData = GetAuthenticationDataAndCulture(); if (authData == null) { throw new UnauthorizedAccessException(); } // Remove whitespace from submitted address (whitespace will probably be entered in some cases) bitcoinAddress = bitcoinAddress.Replace(" ", string.Empty); // Remove a possible start of "bitcoincash:" if (bitcoinAddress.StartsWith("bitcoincash:")) { bitcoinAddress = bitcoinAddress.Substring("bitcoincash:".Length); } if (string.IsNullOrEmpty(authData.CurrentUser.BitcoinPayoutAddress)) { if (!BitcoinUtility.IsValidBitcoinAddress(bitcoinAddress)) { return(new AjaxCallResult { Success = false, DisplayMessage = "Invalid address" }); } authData.CurrentUser.BitcoinPayoutAddress = bitcoinAddress; authData.CurrentUser.BitcoinPayoutAddressTimeSet = DateTime.UtcNow.ToString(CultureInfo.InvariantCulture); // TODO: Create notifications for CEO and for user NotificationCustomStrings strings = new NotificationCustomStrings(); strings["BitcoinAddress"] = bitcoinAddress; OutboundComm userNotify = OutboundComm.CreateNotification(authData.CurrentOrganization, "BitcoinPayoutAddress_Set", strings, People.FromSingle(authData.CurrentUser)); strings["ConcernedPersonName"] = authData.CurrentUser.Canonical; OutboundComm adminNotify = OutboundComm.CreateNotification(authData.CurrentOrganization, "BitcoinPayoutAddress_Set_OfficerNotify", strings); // will send to admins of org as no people specified return(new AjaxCallResult { Success = true }); } else { // If the address is already set return(new AjaxCallResult { Success = false, DisplayMessage = "Address already set" }); } }
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(); }
public void ValidateBitcoinAddressTest() { Assert.IsTrue(BitcoinUtility.IsValidBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i")); // VALID Assert.IsTrue(BitcoinUtility.IsValidBitcoinAddress("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9")); // VALID Assert.IsTrue(BitcoinUtility.IsValidBitcoinAddress("3KS6AuQbZARSvqnaHoHfL1Xhm3bTLFAzoK")); // VALID MULTISIG Assert.IsFalse(BitcoinUtility.IsValidBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X")); // checksum changed, original data Assert.IsFalse(BitcoinUtility.IsValidBitcoinAddress("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i")); // data changed, original checksum Assert.IsFalse(BitcoinUtility.IsValidBitcoinAddress("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i")); // invalid chars Assert.IsFalse(BitcoinUtility.IsValidBitcoinAddress("BZbvjr")); // checksum is fine, address too short }
static public BlockchainTransactionRow FromInsightInfoJson(JObject json, bool inRow = false) { BlockchainTransactionRow newRow = new BlockchainTransactionRow(); newRow.Address = (string)json["addr"]; if (string.IsNullOrEmpty(newRow.Address) && !inRow) { newRow.Address = (string)json["scriptPubKey"]["addresses"][0]; } if (newRow.Address.StartsWith("bitcoincash:")) // make sure we're using the same machine readable format { newRow.Address = BitcoinUtility.EnsureLegacyAddress(newRow.Address); } string valueSatoshisString = (string)json["valueSat"]; if (!string.IsNullOrEmpty(valueSatoshisString)) { newRow.ValueSatoshis = Int64.Parse(valueSatoshisString); } else { // no valueSat field; we must parse a double without loss of precision string valueDoubleString = (string)json["value"]; int indexOfDecimalPoint = valueDoubleString.IndexOf('.'); // The length needs to be eight places past the decimal point, so // zero-pad on the right side until it is while (valueDoubleString.Length <= indexOfDecimalPoint + 8) // less-or-equal is correct here: comparing index and length { valueDoubleString += "0"; // pad to eight decimal places } // We've now made sure that we have the full eight decimal places, so // we can safely remove the decimal point and have an integer string valueDoubleString = valueDoubleString.Replace(".", ""); // Finally, parse as integer string newRow.ValueSatoshis = Int64.Parse(valueDoubleString); } newRow.Index = Int32.Parse((string)json["n"]); newRow.Spent = inRow && (bool)(json["spentTxId"] == null? false: true); return(newRow); }
protected void Page_Load(object sender, EventArgs e) { PageAccessRequired = new Access(CurrentOrganization, AccessAspect.BookkeepingDetails, AccessType.Read); this._authenticationData = GetAuthenticationDataAndCulture(); BitcoinUtility.CheckHotwalletAddresses(_authenticationData.CurrentOrganization); HotBitcoinAddresses addresses = HotBitcoinAddresses.ForOrganization(_authenticationData.CurrentOrganization); Response.ContentType = "application/json"; Response.Output.WriteLine(FormatJson(addresses)); Response.End(); }
private static void OnMondayMorning() { try { Dictionary <int, bool> accountTested = new Dictionary <int, bool>(); // Check the bitcoin hotwallets for forex profit/loss. Organizations allOrganizations = Organizations.GetAll(); foreach (Organization organization in allOrganizations) { FinancialAccount hotWalletAccount = organization.FinancialAccounts.AssetsBitcoinHot; if (hotWalletAccount != null) { BitcoinUtility.CheckHotwalletForexProfitLoss(hotWalletAccount); accountTested[hotWalletAccount.Identity] = true; } } // Detect and log any forex difference exceeding 100 cents. FinancialAccounts allAccounts = FinancialAccounts.GetAll(); // across ALL ORGS! foreach (FinancialAccount account in allAccounts) { // For every account, if it's based on foreign currency, and not already checked, // then check for forex gains/losses if (account.ForeignCurrency != null && !accountTested.ContainsKey(account.Identity)) { account.CheckForexProfitLoss(); } } } catch (Exception e) { ExceptionMail.Send(e, true); if (testMode) { Console.WriteLine(e.ToString()); } } }
private static void OnMondayMorning() { try { // Detect and log any forex difference exceeding 100 cents. FinancialAccounts allAccounts = FinancialAccounts.GetAll(); // across ALL ORGS! foreach (FinancialAccount account in allAccounts) { // For every account, if it's based on foreign currency, check for forex gains/losses if (account.ForeignCurrency != null) { account.CheckForexProfitLoss(); } } // Also check the bitcoin hotwallets for forex profit/loss as a special case of the above. Organizations allOrganizations = Organizations.GetAll(); foreach (Organization organization in allOrganizations) { FinancialAccount hotWalletAccount = organization.FinancialAccounts.AssetsBitcoinHot; if (hotWalletAccount != null) { BitcoinUtility.CheckHotwalletForexProfitLoss(hotWalletAccount); } } } catch (Exception e) { ExceptionMail.Send(e, true); if (testMode) { Console.WriteLine(e.ToString()); } } }
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 }); }
private static void OnNoon() { BotLog.Write(0, "MainCycle", "Noon entry"); try { if (!PilotInstallationIds.IsPilot(PilotInstallationIds.PiratePartySE)) { // PPSE is still running PW4 code for this, so don't run for PPSE Payroll.ProcessMonthly(); // will only actually run on the 1st, but no harm in testing every noon } // Check all bitcoin accounts for previously-unseen transactions once a day Organizations allOrganizations = Organizations.GetAll(); foreach (Organization organization in allOrganizations) { // this actually checks hot storage too, but that's supposed // to be up to date since we're the ones handling it BitcoinUtility.CheckColdStorageForOrganization(organization); } if (!testMode) { /*TestTrace("Running RosterHousekeeping.RemindAllExpiries()..."); * RosterHousekeeping.RemindAllExpiries(); * TestTrace(" done.\r\n");*/ } } catch (Exception e) { TraceAndReport(e); } try { if (!testMode) { /*TestTrace("Running SupportDatabase.NotifyBouncingEmails()..."); * SupportDatabase.NotifyBouncingEmails(); * TestTrace(" done.\r\n");*/ } } catch (Exception e) { TraceAndReport(e); } try { /*TestTrace("Running SupportDatabase.CloseDelayWarnings()..."); * SupportDatabase.CloseDelayWarnings(); * TestTrace(" done.\r\n");*/ } catch (Exception e) { TraceAndReport(e); } try { /*TestTrace("Running SupportMailReview.Run()..."); * SupportMailReview.Run(); * TestTrace(" done.\r\n");*/ } catch (Exception e) { TraceAndReport(e); } BotLog.Write(0, "MainCycle", "Noon exit"); }
private static void Main(string[] args) { // Are we running yet? if (!SystemSettings.DatabaseInitialized) { // will restart the service every 15s until db initialized on OOBE // also, the read of DatabaseInitialized can and will fail if // we're not initalized enough to even have a database throw new InvalidOperationException(); } // Checking for schemata upgrade first of all, after seeing that db exists int startupDbVersion = Database.SwarmDb.DbVersion; DatabaseMaintenance.UpgradeSchemata(); testMode = false; SystemSettings.BackendHostname = Dns.GetHostName(); // Other one-time initializations FinancialTransactions.FixAllUnsequenced(); SupportFunctions.OperatingTopology = OperatingTopology.Backend; // Begin main loop UnixSignal[] killSignals = null; if (!Debugger.IsAttached) { killSignals = new UnixSignal[] { new UnixSignal(Signum.SIGINT), new UnixSignal(Signum.SIGTERM) }; } BotLog.Write(0, "MainCycle", string.Empty); BotLog.Write(0, "MainCycle", "-----------------------------------------------"); BotLog.Write(0, "MainCycle", string.Empty); if (args.Length > 0) { if (args[0].ToLower() == "test") { /* * BotLog.Write(0, "MainCycle", "Running self-tests"); * HeartBeater.Instance.Beat(heartbeatFile); // Otherwise Heartbeater.Beat() will fail in various places * * testMode = true; * Console.WriteLine("Testing All Maintenance Processes (except membership-changing ones)."); * PWLog.Write(PWLogItem.None, 0, PWLogAction.SystemTest, string.Empty, string.Empty); * * Console.WriteLine("\r\n10-second intervals:"); * OnEveryTenSeconds(); * Console.WriteLine("\r\nEvery minute:"); * OnEveryMinute(); * Console.WriteLine("\r\nEvery five minutes:"); * OnEveryFiveMinutes(); * Console.WriteLine("\r\nEvery hour:"); * OnEveryHour(); * Console.WriteLine("\r\nNoon:"); * OnNoon(); * Console.WriteLine("\r\nMidnight:"); * OnMidnight(); */ Console.WriteLine("Testing database access..."); Console.WriteLine(SwarmDb.GetDatabaseForReading().GetPerson(1).Name); Console.WriteLine(SwarmDb.GetDatabaseForReading().GetPerson(1).PasswordHash); Console.WriteLine("Creating OutboundComm..."); OutboundComm.CreateNotification(null, NotificationResource.System_Startup_Backend); Console.WriteLine("Transmitting..."); OutboundComms comms = OutboundComms.GetOpen(); Console.WriteLine("{0} open items in outbound comms.", comms.Count); foreach (OutboundComm comm in comms) { if (comm.TransmitterClass != "Swarmops.Utility.Communications.CommsTransmitterMail") { throw new NotImplementedException(); } ICommsTransmitter transmitter = new CommsTransmitterMail(); OutboundCommRecipients recipients = comm.Recipients; PayloadEnvelope envelope = PayloadEnvelope.FromXml(comm.PayloadXml); foreach (OutboundCommRecipient recipient in recipients) { transmitter.Transmit(envelope, recipient.Person); } } Console.Write("\r\nAll tests run. Waiting for mail queue to flush... "); while (!MailTransmitter.CanExit) { Thread.Sleep(50); } Console.WriteLine("done."); BotLog.Write(0, "MainCycle", "Exiting self-tests"); return; } if (args[0].ToLower() == "console") { Console.WriteLine("\r\nRunning Swarmops-Backend in CONSOLE mode.\r\n"); // ------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------- // ----------------------- INSERT ANY ONE-OFF ACTIONS HERE ------------------------- Console.Write("\r\nWaiting for mail queue to flush... "); while (!MailTransmitter.CanExit) { Thread.Sleep(50); } Console.WriteLine("done."); return; } if (args[0].ToLowerInvariant() == "pdfregen") { if (args.Length > 1) { int docId = Int32.Parse(args[1]); PdfProcessor.Rerasterize(Document.FromIdentity(docId)); } else { Console.WriteLine("Regenerating all bitmaps from PDF uploads."); PdfProcessor.RerasterizeAll(); Console.WriteLine("Done."); } return; } if (args[0].ToLower() == "rsm") { Console.WriteLine("Testing character encoding: räksmörgås RÄKSMÖRGÅS"); return; } } /* * MailMessage message = new MailMessage(); * message.From = new MailAddress(Strings.MailSenderAddress, Strings.MailSenderName); * message.To.Add (new MailAddress ("*****@*****.**", "Rick Falkvinge (Piratpartiet)")); * message.Subject = "Räksmörgåsarnas ékÖNÖMÏåvdëlnïng"; * message.Body = "Hejsan hoppsan Räksmörgåsar."; * message.BodyEncoding = Encoding.Default; * message.SubjectEncoding = Encoding.Default; * * SmtpClient smtpClient = new SmtpClient ("localhost"); * smtpClient.Credentials = null; // mono bug * smtpClient.Send (message);*/ Console.WriteLine(" * Swarmops Backend starting"); BotLog.Write(0, "MainCycle", "Backend STARTING"); // Disable certificate checking due to Mono not installing with a certificate repository - this is UTTERLY broken SupportFunctions.DisableSslCertificateChecks(); // MONO BUG/MISFEATURE: Mono has no root certificates, so can't verify cert // Tell sysop we're starting OutboundComm.CreateNotification(null, NotificationResource.System_Startup_Backend); // Check for existence of installation ID. If not, create one. Warning: has privacy implications when communicated. if (Persistence.Key["SwarmopsInstallationId"] == string.Empty) { Persistence.Key["SwarmopsInstallationId"] = Guid.NewGuid().ToString(); } // Check for existence of bitcoin hotwallet root BitcoinUtility.VerifyBitcoinHotWallet(); // Initialize backend socket server int backendSocketPort = SystemSettings.WebsocketPortBackend; _socketServer = new WebSocketServer(backendSocketPort); _socketServer.AddWebSocketService <BackendServices>("/Backend"); _socketServer.Start(); // Initialize socket client to Blockchain.Info (pending our own services) using ( _blockChainInfoSocket = new WebSocket("wss://ws.blockchain.info/inv?api_code=" + SystemSettings.BlockchainSwarmopsApiKey)) { // Begin maintenance loop DateTime cycleStartTime = DateTime.UtcNow; DateTime cycleEndTime; int lastSecond = cycleStartTime.Second; int lastMinute = cycleStartTime.Minute; int lastHour = cycleStartTime.Hour; bool exitFlag = false; _blockChainInfoSocket.OnOpen += new EventHandler(OnBlockchainOpen); _blockChainInfoSocket.OnError += new EventHandler <ErrorEventArgs>(OnBlockchainError); _blockChainInfoSocket.OnClose += new EventHandler <CloseEventArgs>(OnBlockchainClose); _blockChainInfoSocket.OnMessage += new EventHandler <MessageEventArgs>(OnBlockchainMessage); _blockChainInfoSocket.Connect(); while (!exitFlag) // exit is handled by signals handling at end of loop { BotLog.Write(0, "MainCycle", "Cycle Start"); cycleStartTime = DateTime.UtcNow; cycleEndTime = cycleStartTime.AddSeconds(10); try { OnEveryTenSeconds(); if (cycleStartTime.Second < lastSecond) { OnEveryMinute(); if (cycleStartTime.Minute % 5 == 0) { OnEveryFiveMinutes(); } } if (cycleStartTime.Minute < lastMinute) { OnEveryHour(); if (DateTime.Now.Hour == 10 && DateTime.Today.DayOfWeek == DayOfWeek.Tuesday) { OnTuesdayMorning(); } if (DateTime.Now.Hour == 7 && DateTime.Today.DayOfWeek == DayOfWeek.Monday) { OnMondayMorning(); } } if (cycleStartTime.Hour >= 12 && lastHour < 12) { OnNoon(); } if (cycleStartTime.Hour < lastHour) { OnMidnight(); } } catch (Exception e) { // Note each "OnEvery..." catches its own errors and sends Exception mails, // so that failure in one should not stop the others from running. This particular // code should never run. ExceptionMail.Send(new Exception("Failed in swarmops-backend main loop", e), true); } lastSecond = cycleStartTime.Second; lastMinute = cycleStartTime.Minute; lastHour = cycleStartTime.Hour; // Wait for a maximum of ten seconds (the difference between cycleStartTime and cycleEndTime) int iterationCount = 0; DateTime utcNow = DateTime.UtcNow; while (utcNow < cycleEndTime && !exitFlag) { int signalIndex = 250; // Handle important service orders (those that can't be lost in a random loss // of connection of a socket): BackendServiceOrders backendOrders = BackendServiceOrders.GetNextBatch(5); backendOrders.Execute(); // takes at most 250ms per BSO reqs // Block until a SIGINT or SIGTERM signal is generated, or 1/4 second has passed. // However, we can't do that in a development environment - it won't have the // Mono.Posix assembly, and won't understand UnixSignals. So people running this in // a dev environment will need to stop it manually. if (!Debugger.IsAttached) { signalIndex = UnixSignal.WaitAny(killSignals, 250); } else { TimeSpan timeLeft = (cycleEndTime - utcNow); BotLog.Write(0, "MainCycle Debug", string.Format(CultureInfo.InvariantCulture, "Waiting for {0:F2} more seconds for cycle end", timeLeft.TotalMilliseconds / 1000.0)); Thread.Sleep(250); } if (signalIndex < 250) { exitFlag = true; Console.WriteLine("Caught signal " + killSignals[signalIndex].Signum + ", exiting"); BotLog.Write(0, "MainCycle", "EXIT SIGNAL (" + killSignals[signalIndex].Signum + "), terminating backend"); } utcNow = DateTime.UtcNow; // Every second, send an internal heartbeat if (iterationCount++ % 4 == 0) { InternalHeartbeat(); } } } } Console.WriteLine(" * Swarmops Backend stopping"); BotLog.Write(0, "MainCycle", "BACKEND EXITING, sending backend-termination notices"); /* * if (HeartBeater.Instance.WasKilled) * { * // removed unconditional delete, cron job that restarts bot uses it to know that it is intentionally down. * ExceptionMail.Send(new Exception("HeartBeater triggered restart of Swarmops Backend. Will commence after 800 seconds."), false); * }*/ BotLog.Write(0, "MainCycle", "...done"); /* * while (!MailTransmitter.CanExit) * { * System.Threading.Thread.Sleep(50); * }*/ _socketServer.Stop(); Thread.Sleep(2000); }
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 }); }
static void Main(/*string[] args*/) { // Establish that we have a database connection Person testPerson = Person.FromIdentity(1); testPerson.GetSecureAvatarLink(64); // suppress "is never used" warning Organization organization = Organization.FromIdentity(7); // TODO: // 0) Check ForeignCurrency of hotwallet account FinancialAccount hotWallet = organization.FinancialAccounts.AssetsBitcoinHot; if (hotWallet != null) { Console.WriteLine("Organization has Hotwallet"); if (hotWallet.ForeignCurrency != null) { if (!hotWallet.ForeignCurrency.IsBitcoinCash) { Console.WriteLine(" - Foreign Currency was incorrect; setting to BCH"); hotWallet.ForeignCurrency = Currency.BitcoinCash; } } else { // ForeignCurrency of hotwallet was Null hotWallet.ForeignCurrency = Currency.BitcoinCash; Console.WriteLine(" - Foreign Currency was not set; setting to BCH"); } } // 0.5) Re-register the initial Sandbox Echo address /* * HotBitcoinAddress.Create(organization, BitcoinChain.Cash, * BitcoinUtility.BitcoinEchoTestIndex, 1);*/ // 1) Check unspents on hotwallet addresses /* * HotBitcoinAddresses addresses = HotBitcoinAddresses.ForOrganization(organization); * * foreach (HotBitcoinAddress address in addresses) * { * HotBitcoinAddressUnspents unspents1 = HotBitcoinAddressUnspents.ForAddress(address); * int beforeCheckCount = unspents1.Count(); * * BitcoinUtility.TestUnspents(BitcoinChain.Core, address.ProtocolLevelAddress); * BitcoinUtility.TestUnspents(BitcoinChain.Cash, address.ProtocolLevelAddress); * * HotBitcoinAddressUnspents unspents2 = HotBitcoinAddressUnspents.ForAddress(address); * int correctedCount = unspents2.Count(); * * if (beforeCheckCount != correctedCount) * { * Console.WriteLine(" - Unspent count was corrected: was {0}, changed to {1}", beforeCheckCount, correctedCount); * } * * if ( * address.DerivationPath.StartsWith(BitcoinUtility.BitcoinEchoTestIndex.ToString() + " ")) * { * // This is an address that should be echoed back, all of it * * foreach (HotBitcoinAddressUnspent unspent in unspents2) * { * ReturnBitcoinEchoUtxoOrder backendOrder = new ReturnBitcoinEchoUtxoOrder(unspent); * backendOrder.Create(organization, testPerson); * } * } * }*/ // 2) Check cold storage accounts, make sure there are corresponding Cash accounts for all Core accounts /* * FinancialAccount coldStorageRoot = organization.FinancialAccounts.AssetsBitcoinCold; * * if (coldStorageRoot != null) * { * FinancialAccounts accounts = coldStorageRoot.ThisAndBelow(); * * foreach (FinancialAccount account in accounts) * { * string bitcoinAddress = account.BitcoinAddress; * * if (!String.IsNullOrEmpty(bitcoinAddress)) * { * Currency accountCurrency = organization.Currency; * if (account.ForeignCurrency != null) * { * accountCurrency = account.ForeignCurrency; * } * * if (accountCurrency.IsBitcoinCore) * { * // Assert there's a corresponding Bitcoin Cash account * * bool bitcoinCashExists = false; * * FinancialAccounts accountsMatchingAddress = * FinancialAccounts.FromBitcoinAddress(bitcoinAddress); * * foreach (FinancialAccount accountMatchingAddress in accountsMatchingAddress) * { * if (accountMatchingAddress.Identity == account.Identity) * { * continue; // this is the outer loop account we've found in the inner loop * } * * if (accountMatchingAddress.OrganizationId != account.OrganizationId) * { * // This is not supposed to happen, ever, since it implies that two * // different organizations share the same bitcoin address. Nevertheless * // it's a theoretically valid case and so we check for it * * continue; // not the right organization * } * * if (accountMatchingAddress.ForeignCurrency.IsBitcoinCash) * { * // We have a match for organization, currency, and address * * bitcoinCashExists = true; * } * } * * if (!bitcoinCashExists) * { * // Need to create a new Bitcoin Cash address and populate it with transactions, * // starting on 2017-Dec-30 * * if (!account.Name.StartsWith("[Core] ")) * { * account.Name = "[Core] " + account.Name; * } * * FinancialAccount correspondingCashAccount = FinancialAccount.Create( * account.Organization, "[Cash] " + account.Name.Substring(7), account.AccountType, * account.Parent); * * correspondingCashAccount.BitcoinAddress = account.BitcoinAddress; * * if (!organization.Currency.IsBitcoinCash) * { * correspondingCashAccount.ForeignCurrency = Currency.BitcoinCash; * } * } * } * } * } * }*/ // 2½ - TODO) Shapeshift all Core into Cash // 2 3/4 -- Check cold storage for the new Cash accounts BitcoinUtility.CheckColdStorageForOrganization(organization); // 3) Adjust balances of foreign cents on hotwallet // 4) Adjust value of hotwallet }