// Loops through ALL markets, and calls pMarket->GetNym_OfferList(NYM_ID, *pOfferList) for each. // Returns a list of all the offers that a specific Nym has on all the markets. // bool OTCron::GetNym_OfferList(OTASCIIArmor & ascOutput, const OTIdentifier & NYM_ID, int & nOfferCount) { nOfferCount = 0; // Outputs the number of offers on this nym. // --------------------------- OTDB::OfferListNym * pOfferList = dynamic_cast<OTDB::OfferListNym*>(OTDB::CreateObject(OTDB::STORED_OBJ_OFFER_LIST_NYM)); OTCleanup<OTDB::OfferListNym> theListAngel(*pOfferList); // ----------------------------------------------------------- FOR_EACH(mapOfMarkets, m_mapMarkets) { OTMarket * pMarket = (*it).second; OT_ASSERT(NULL != pMarket); int nNymOfferCount = 0; if (false == pMarket->GetNym_OfferList(NYM_ID, *pOfferList, nNymOfferCount)) // appends to *pOfferList, each iteration. { // may wish to add a log later. Anyway, keep iterationg and appending, then send back whatever we have. } else // Success! nOfferCount += nNymOfferCount; }
// OTCron IS responsible for cleaning up theMarket, and takes ownership. // So make SURE it is allocated on the HEAP before you pass it in here, and // also make sure to delete it again if this call fails! bool OTCron::AddMarket(OTMarket & theMarket, bool bSaveMarketFile/*=true*/) { OT_ASSERT(NULL != GetServerNym()); theMarket.SetCronPointer(*this); // This way every Market has a pointer to Cron. OTIdentifier MARKET_ID(theMarket); OTString str_MARKET_ID(MARKET_ID); std::string std_MARKET_ID = str_MARKET_ID.Get(); // See if there's something else already there with the same market ID. mapOfMarkets::iterator ii = m_mapMarkets.find(std_MARKET_ID); // If it's not already on the list, then add it... if ( ii == m_mapMarkets.end() ) { // If I've been instructed to save the market, and Cron did NOT successfully save the market // (to its own file), then return false. This will happen if filesystem problems. if (bSaveMarketFile && !theMarket.SaveMarket()) { OTLog::vError("Error saving market file while adding new Market to Cron:\n%s\n", std_MARKET_ID.c_str()); return false; } m_mapMarkets[std_MARKET_ID] = &theMarket; bool bSuccess = true; // When Cron serializes, it stores a list of all its markets in the main cron file. // The actual markets themselves serialize separately to the market folder. // if (bSaveMarketFile) // This executes only the first time that a market is added to Cron. // (versus when it's just being reloaded from file and added back to the internal list.) { // Since we added a market to the Cron, we SAVE it. bSuccess = SaveCron(); // If we're loading from file, and bSaveMarketFile is false, I don't want to save here. that's why it's in this block. if (bSuccess) OTLog::Output(3, "New Market has been added to Cron.\n"); else OTLog::Error("Error saving while adding new Market to Cron.\n"); } return bSuccess; } // Otherwise, if it was already there, log an error. else { OTLog::vError("Attempt to add Market that was already there: %s\n", std_MARKET_ID.c_str()); } return false; }
void OTCron::UpdateContents() { // I release this because I'm about to repopulate it. m_xmlUnsigned.Release(); m_xmlUnsigned.Concatenate("<?xml version=\"%s\"?>\n\n", "1.0"); // ------------------------------------------------------------- const OTString SERVER_ID(m_SERVER_ID); m_xmlUnsigned.Concatenate("<cron\n version=\"%s\"\n" " serverID=\"%s\"" " >\n\n", m_strVersion.Get(), SERVER_ID.Get()); // ------------------------------------------------------------- // Save the Market entries (the markets themselves are saved in a markets folder.) OTMarket * pMarket = NULL; for (mapOfMarkets::iterator ii = m_mapMarkets.begin(); ii != m_mapMarkets.end(); ++ii) { pMarket = (*ii).second; OT_ASSERT(NULL != pMarket); OTIdentifier MARKET_ID(*pMarket); OTString str_MARKET_ID(MARKET_ID); OTString str_ASSET_ID(pMarket->GetAssetID()); OTString str_CURRENCY_ID(pMarket->GetCurrencyID()); m_xmlUnsigned.Concatenate("<market\n marketID=\"%s\"\n" " assetID=\"%s\"\n" " currencyID=\"%s\"\n" " marketScale=\"%ld\"" " >\n\n", str_MARKET_ID.Get(), str_ASSET_ID.Get(), str_CURRENCY_ID.Get(), pMarket->GetScale()); } // ------------------------------------------------------------- // Save the Cron Items OTCronItem * pItem = NULL; for (mapOfCronItems::iterator ii = m_mapCronItems.begin(); ii != m_mapCronItems.end(); ++ii) { pItem = (*ii).second; OT_ASSERT(NULL != pItem); OTString strItem(*pItem); // Extract the cron item contract into string form. OTASCIIArmor ascItem(strItem); // Base64-encode that for storage. m_xmlUnsigned.Concatenate("<cronItem>\n%s</cronItem>\n\n", ascItem.Get()); } // ------------------------------------------------------------- // Save the transaction numbers. long lTransactionNumber = 0; for (listOfTransactionNumbers::iterator iii = m_listTransactionNumbers.begin(); iii != m_listTransactionNumbers.end(); ++iii) { lTransactionNumber = *iii; m_xmlUnsigned.Concatenate("<transactionNum value=\"%ld\" />\n\n", lTransactionNumber); } // for // ------------------------------------------------------------- m_xmlUnsigned.Concatenate("</cron>\n"); }
// return -1 if error, 0 if nothing, and 1 if the node was processed. int OTCron::ProcessXMLNode(irr::io::IrrXMLReader*& xml) { OT_ASSERT(NULL != GetServerNym()); int nReturnVal = 0; // Here we call the parent class first. // If the node is found there, or there is some error, // then we just return either way. But if it comes back // as '0', then nothing happened, and we'll continue executing. // // -- Note you can choose not to call the parent if // you don't want to use any of those xml tags. // As I do below, in the case of OTAccount. //if (nReturnVal = OTContract::ProcessXMLNode(xml)) // return nReturnVal; if (!strcmp("cron", xml->getNodeName())) { m_strVersion = xml->getAttributeValue("version"); // --------------------- const OTString strServerID(xml->getAttributeValue("serverID")); m_SERVER_ID.SetString(strServerID); // --------------------- OTLog::vOutput(0, "\n\nLoading an OTCron object. ServerID:\n%s\n", strServerID.Get()); nReturnVal = 1; } else if (!strcmp("transactionNum", xml->getNodeName())) { long lTransactionNum = 0; lTransactionNum = atol(xml->getAttributeValue("value")); OTLog::vOutput(1, "Transaction Number %ld available for Cron.\n", lTransactionNum); AddTransactionNumber(lTransactionNum); // This doesn't save to disk. Make sure to save Cron when it changes. nReturnVal = 1; } else if (!strcmp("cronItem", xml->getNodeName())) { OTString strData; if (!LoadEncodedTextField(xml, strData) || !strData.Exists()) { OTLog::Error("Error in OTCron::ProcessXMLNode: cronItem field without value.\n"); return (-1); // error condition } else { OTCronItem * pItem = OTCronItem::NewCronItem(strData); if (NULL == pItem) { OTLog::Error("Unable to create cron item from data in cron file.\n"); return (-1); } if (AddCronItem(*pItem, false)) // bSaveReceipt=false. The receipt is only saved once: When item FIRST added to cron. { // But here, the item was ALREADY in cron, and is merely being loaded from disk. Thus, // it would be wrong to try to create the "original record" as if it were brand new // and still had the user's signature on it. (Once added to Cron, the signatures are // released and the SERVER signs it from there. That's why the user's version is saved // as a receipt in the first place -- so we have a record of the user's authorization.) OTLog::Output(2, "Successfully loaded cron item and added to list.\n"); } else { OTLog::Error("Though loaded successfully, unable to add cron item (from cron file) to cron list.\n"); delete pItem; pItem = NULL; return (-1); } } nReturnVal = 1; } else if (!strcmp("market", xml->getNodeName())) { const OTString strMarketID(xml->getAttributeValue("marketID")); const OTString strAssetID(xml->getAttributeValue("assetID")); const OTString strCurrencyID(xml->getAttributeValue("currencyID")); const long lScale = atol(xml->getAttributeValue("marketScale")); const OTIdentifier ASSET_ID(strAssetID), CURRENCY_ID(strCurrencyID); OTLog::vOutput(1, "Loaded cron entry for Market:\n%s.\n", strMarketID.Get()); // LoadMarket() needs this info to do its thing. OTMarket * pMarket = new OTMarket(m_SERVER_ID, ASSET_ID, CURRENCY_ID, lScale); OT_ASSERT(NULL != pMarket); pMarket->SetCronPointer(*this); // This way every Market has a pointer to Cron. // AddMarket normally saves to file, but we don't want that when we're LOADING from file, now do we? if (!pMarket->LoadMarket() || !pMarket->VerifySignature(*GetServerNym()) || !AddMarket(*pMarket, false)) // bSaveFile=false: don't save this file WHILE loading it!!! { OTLog::Error("Somehow error while loading, verifying, or adding market while loading Cron file.\n"); delete pMarket; pMarket = NULL; return (-1); } else { OTLog::Output(1, "Loaded market entry from cronfile, and also loaded the market file itself.\n"); } nReturnVal = 1; } return nReturnVal; }
bool OTCron::GetMarketList (OTASCIIArmor & ascOutput, int & nMarketCount) { nMarketCount = 0; // This parameter is set to zero here, and incremented in the loop below. // ------------------------ OTMarket * pMarket = NULL; OTDB::MarketList * pMarketList = dynamic_cast<OTDB::MarketList*>(OTDB::CreateObject(OTDB::STORED_OBJ_MARKET_LIST)); OTCleanup<OTDB::MarketList> theListAngel(*pMarketList); // ----------------------------------------------------------- for (mapOfMarkets::iterator ii = m_mapMarkets.begin(); ii != m_mapMarkets.end(); ++ii) { pMarket = (*ii).second; OT_ASSERT(NULL != pMarket); OTDB::MarketData * pMarketData = dynamic_cast<OTDB::MarketData *>(OTDB::CreateObject(OTDB::STORED_OBJ_MARKET_DATA)); OTCleanup<OTDB::MarketData> theDataAngel(*pMarketData); // -------------------------------------------- const OTIdentifier MARKET_ID(*pMarket); const OTString str_MARKET_ID(MARKET_ID); const OTString str_ServerID(pMarket->GetServerID()); const OTString str_ASSET_ID(pMarket->GetAssetID()); const OTString str_CURRENCY_ID(pMarket->GetCurrencyID()); pMarketData->server_id = str_ServerID.Get(); pMarketData->market_id = str_MARKET_ID.Get(); pMarketData->asset_type_id = str_ASSET_ID.Get(); pMarketData->currency_type_id = str_CURRENCY_ID.Get(); // -------------------------------------------- const long & lScale = pMarket->GetScale(); pMarketData->scale = to_string<long>(lScale); // -------------------------------------------- const uint64_t theCurrentBid = pMarket->GetHighestBidPrice(); const uint64_t theCurrentAsk = pMarket->GetLowestAskPrice(); pMarketData->current_bid = to_string<uint64_t>(theCurrentBid); pMarketData->current_ask = to_string<uint64_t>(theCurrentAsk); // --------------------------------------------- const long & lLastSalePrice = pMarket->GetLastSalePrice(); const long & lTotalAvailableAssets = pMarket->GetTotalAvailableAssets(); pMarketData->total_assets = to_string<long>(lTotalAvailableAssets); pMarketData->last_sale_price = to_string<long>(lLastSalePrice); // --------------------------------------------- const mapOfOffers::size_type theBidCount = pMarket->GetBidCount(); const mapOfOffers::size_type theAskCount = pMarket->GetAskCount(); pMarketData->number_bids = to_string<mapOfOffers::size_type>(theBidCount); pMarketData->number_asks = to_string<mapOfOffers::size_type>(theAskCount); // --------------------------------------------- // In the past 24 hours. // (I'm not collecting this data yet, (maybe never), so these values aren't set at all.) // // pMarketData->volume_trades = ???; // pMarketData->volume_assets = ???; // pMarketData->volume_currency = ???; // // pMarketData->recent_highest_bid = ???; // pMarketData->recent_lowest_ask = ???; // --------------------------------------------- // *pMarketData is CLONED at this time (I'm still responsible to delete.) // That's also why I add it here, below: So the data is set right before the cloning occurs. // pMarketList->AddMarketData(*pMarketData); nMarketCount++; } // ------------------------------------------------------------- // Now pack the list into strOutput... if (nMarketCount == 0) return true; // Success, but the list contains 0 markets. else if (nMarketCount > 0) { OTDB::Storage * pStorage = OTDB::GetDefaultStorage(); OT_ASSERT(NULL != pStorage); OTDB::OTPacker * pPacker = pStorage->GetPacker(); // No need to check for failure, since this already ASSERTS. No need to cleanup either. // ----------------------------- OTDB::PackedBuffer * pBuffer = pPacker->Pack(*pMarketList); // Now we PACK our market list. if (NULL == pBuffer) { OTLog::Error("Failed packing pMarketList in OTCron::GetMarketList. \n"); return false; } OTCleanup<OTDB::PackedBuffer> theBufferAngel(*pBuffer); // make sure memory is cleaned up. // -------------------------------------------------------- // Now we need to translate pBuffer into strOutput. const uint8_t* pUint = static_cast<const uint8_t*>(pBuffer->GetData()); const size_t theSize = pBuffer->GetSize(); if ((theSize > 0) && (NULL != pUint)) { OTData theData(pUint, theSize); // This function will base64 ENCODE theData, // and then Set() that as the string contents. ascOutput.SetData(theData); // bool bSuccessSetData = false; // bSuccessSetData = ascOutput.SetData(theData); return true; } else OTLog::Error("OTCron::GetMarketList: 0 size, or null return value, while getting raw data from packed buffer.\n"); } else OTLog::vError("OTCron::GetMarketList: nMarketCount is less than zero: %d.\n", nMarketCount); return false; }
// Loops through ALL markets, and calls pMarket->GetNym_OfferList(NYM_ID, *pOfferList) for each. // Returns a list of all the offers that a specific Nym has on all the markets. // bool OTCron::GetNym_OfferList(OTASCIIArmor & ascOutput, const OTIdentifier & NYM_ID, int & nOfferCount) { nOfferCount = 0; // Outputs the number of offers on this nym. // --------------------------- OTDB::OfferListNym * pOfferList = dynamic_cast<OTDB::OfferListNym*>(OTDB::CreateObject(OTDB::STORED_OBJ_OFFER_LIST_NYM)); OTCleanup<OTDB::OfferListNym> theListAngel(*pOfferList); // ----------------------------------------------------------- for (mapOfMarkets::iterator ii = m_mapMarkets.begin(); ii != m_mapMarkets.end(); ++ii) { OTMarket * pMarket = (*ii).second; OT_ASSERT(NULL != pMarket); int nNymOfferCount = 0; if (false == pMarket->GetNym_OfferList(NYM_ID, *pOfferList, nNymOfferCount)) // appends to *pOfferList, each iteration. { // may wish to add a log later. Anyway, keep iterationg and appending, then send back whatever we have. } else // Success! nOfferCount += nNymOfferCount; } // ------------------------------------------------------------- // Now pack the list into strOutput... if (nOfferCount == 0) return true; // Success, but 0 offers being returned. (List is empty.) else if (nOfferCount > 0) { OTDB::Storage * pStorage = OTDB::GetDefaultStorage(); OT_ASSERT(NULL != pStorage); OTDB::OTPacker * pPacker = pStorage->GetPacker(); // No need to check for failure, since this already ASSERTS. No need to cleanup either. // ----------------------------- OTDB::PackedBuffer * pBuffer = pPacker->Pack(*pOfferList); // Now we PACK our nym's offer list. if (NULL == pBuffer) { OTLog::Error("Failed packing pOfferList in OTCron::GetNym_OfferList. \n"); return false; } OTCleanup<OTDB::PackedBuffer> theBufferAngel(*pBuffer); // make sure memory is cleaned up. // -------------------------------------------------------- // Now we need to translate pBuffer into strOutput. const uint8_t* pUint = static_cast<const uint8_t*>(pBuffer->GetData()); const size_t theSize = pBuffer->GetSize(); if ((NULL != pUint) || (theSize < 2)) { OTData theData(pUint, theSize); // This function will base64 ENCODE theData, // and then Set() that as the string contents. ascOutput.SetData(theData); return true; } else OTLog::Error("Null returned, or bad size, while getting buffer data in OTCron::GetNym_OfferList.\n"); } else OTLog::vError("Error: Less-than-zero nOfferCount in OTCron::GetNym_OfferList: %d.\n", nOfferCount); return false; }
// Cron only removes an item when that item REQUESTS to be removed (by setting // the flag.) // Once this happens, Cron has full permission to remove it. Thus, this hook is // forceful. It // is cron saying, YOU ARE BEING REMOVED. Period. So cleanup whatever you have // to clean up. // // In this case, it removes the corresponding offer from the market. // void OTTrade::onRemovalFromCron() { OTCron* cron = GetCron(); OT_ASSERT(cron != nullptr); // If I don't already have an offer on the market, then I will have trouble // figuring out // my SCALE, which is stored on the Offer. Therefore I will instantiate an // offer (since I // store the original internally) and I will look up the scale. // int64_t scale = 1; // todo stop hardcoding. int64_t transactionNum = 0; if (offer_ != nullptr) { if (!marketOffer_.Exists()) { otErr << "OTTrade::onRemovalFromCron called with nullptr offer_ and " "empty marketOffer_.\n"; return; } std::unique_ptr<OTOffer> offer(new OTOffer()); // Trying to load the offer from the trader's original signed request // (So I can use it to lookup the Market ID, so I can see if the offer // is // already there on the market.) if (!offer->LoadContractFromString(marketOffer_)) { otErr << "Error loading offer from string in " "OTTrade::onRemovalFromCron\n"; return; } scale = offer->GetScale(); transactionNum = offer->GetTransactionNum(); } else { scale = offer_->GetScale(); transactionNum = offer_->GetTransactionNum(); } OTMarket* market = cron->GetOrCreateMarket(GetInstrumentDefinitionID(), GetCurrencyID(), scale); // Couldn't find (or create) the market. // if (market == nullptr) { otErr << "Unable to find market within requested parameters in " "OTTrade::onRemovalFromCron.\n"; return; } // // Let's see if the offer is ALREADY allocated and on this market! // OTOffer* marketOffer = market->GetOffer(transactionNum); // The Offer is already on the Market. // if (marketOffer != nullptr) { offer_ = marketOffer; offer_->SetTrade(*this); } market->RemoveOffer(transactionNum); }
// Assuming the offer is ON the market, this will get the pointer to that offer. // Otherwise it will try to add it to the market. // Otherwise it will fail. (Perhaps it's a stop order, and not ready to activate // yet.) // OTOffer* OTTrade::GetOffer(Identifier* offerMarketId, OTMarket** market) { OT_ASSERT(GetCron() != nullptr); // See if the offer has already been instantiated onto a market... if (offer_ != nullptr) { offer_->SetTrade(*this); // Probably don't need this line. I'll remove // it someday while optimizing. // In fact since it should already be set, having this here would // basically // hide it from me if the memory was ever walked on from a bug // somewhere. // It loaded. Let's get the Market ID off of it so we can locate the // market. const Identifier OFFER_MARKET_ID(*offer_); if (market != nullptr) { OTMarket* pMarket = GetCron()->GetMarket(OFFER_MARKET_ID); // Sometimes the caller function would like a copy of this market // pointer, when available. // So I pass it back to him here, if he wants. That way he doesn't // have to do this work again // to look it up. if (pMarket != nullptr) *market = pMarket; // <================= else otErr << "OTTrade::" << __FUNCTION__ << ": offer_ already exists, yet unable to find the " "market it's supposed to be on.\n"; } if (offerMarketId != nullptr) { // Sometimes the caller function would like a copy of this ID. So I // give the option to pass in a pointer so I can give it here. offerMarketId->Assign(OFFER_MARKET_ID); } return offer_; } // if offer_ ALREADY EXISTS. // else (BELOW) offer_ IS nullptr, and thus it didn't exist yet... if (!marketOffer_.Exists()) { otErr << "OTTrade::GetOffer called with empty marketOffer_.\n"; return nullptr; } OTOffer* offer = new OTOffer(); OT_ASSERT(offer != nullptr); // Trying to load the offer from the trader's original signed request // (So I can use it to lookup the Market ID, so I can see the offer is // already there on the market.) if (!offer->LoadContractFromString(marketOffer_)) { otErr << "Error loading offer from string in OTTrade::GetOffer\n"; delete offer; offer = nullptr; return nullptr; } // No need to do any additional security verification here on the Offer, // since the Offer is already heavily verified in // OTServer::NotarizeMarketOffer(). // So as long as you feel safe about the Trade, then you can feel safe about // the Offer already, with no further checks. // *Also remember we saved a copy of the original in the cron folder. // It loaded. Let's get the Market ID off of it so we can locate the market. Identifier OFFER_MARKET_ID(*offer); if (offerMarketId != nullptr) { // Sometimes the caller function would like a copy of this ID. So I // give the option to pass in a pointer so I can give it here. offerMarketId->Assign(OFFER_MARKET_ID); } // Previously if a user tried to use a market that didn't exist, I'd just // return failure. // But now we will create any market that doesn't already exist. // (Remember, the server operator could just erase the market folder--it // wouldn't // affect anyone's balances!) Update: he probably couldn't just wipe the // markets folder, // actually, without making it impossible for certain Nyms to get rid of // certain issued #s. // // OTMarket * pMarket = m_cron->GetMarket(OFFER_MARKET_ID); OTMarket* pMarket = GetCron()->GetOrCreateMarket( GetInstrumentDefinitionID(), GetCurrencyID(), offer->GetScale()); // Couldn't find (or create) the market. if (pMarket == nullptr) { otOut << "OTTrade::" << __FUNCTION__ << ": Unable to find or create market within requested parameters."; delete offer; offer = nullptr; return nullptr; } // If the caller passed in the address of a market pointer (optional) if (market != nullptr) { // Sometimes the caller function would like a copy of this market // pointer, when available. // So I pass it back to him here, if he wants. That way he doesn't have // to do this work again // to look it up. *market = pMarket; } // At this point, I have heap-allocated the offer, used it to get the Market // ID, and successfully // used that to get a pointer to the market matching that ID. // // Let's see if the offer is ALREADY allocated and on this market! // If so, delete the one I just allocated. If not, add it to the market. OTOffer* marketOffer = pMarket->GetOffer(offer->GetTransactionNum()); // The Offer is already on the Market. // NOTE: It may just start out this way, without ever being added. // How is that possible? Because maybe it was in the market file when we // first loaded up, // and had been added on some previous run of the software. So since we // started running, // the pMarket->AddOffer() code below has literally never run for that // offer. Instead we // first find it here, and thus return the pointer before getting any // farther. // // IN ALL CASES, we make sure to call offer_->SetTrade() so that it has a // pointer BACK to // this Trade object! (When actually processing the offer, the market will // need the account // numbers and Nym IDs... which are stored here on the trade.) if (marketOffer != nullptr) { offer_ = marketOffer; // Since the Offer already exists on the market, no need anymore for the // one we allocated above (to get the market ID.) So we delete it now. delete offer; offer = nullptr; offer_->SetTrade(*this); return offer_; } // Okay so the offer ISN'T already on the market. If it's not a stop order, // let's ADD the one we // allocated to the market now! (Stop orders are activated through their own // logic, which is below // this, in the else block.) // if (!IsStopOrder()) { if (hasTradeActivated_) { // Error -- how has the trade already activated, yet not on the // market and null in my pointer? otErr << "How has the trade already activated, yet not on the " "market and null in my pointer?\n"; } else if (!pMarket->AddOffer(this, *offer, true)) // Since we're // actually adding // an offer to the // market (not just { // loading from disk) the we actually want to save the market. // bSaveFile=true. // Error adding the offer to the market! otErr << "Error adding the offer to the market! (Even though " "supposedly the right market.)\n"; } else { // SUCCESS! offer_ = offer; hasTradeActivated_ = true; // The Trade (stored on Cron) has a copy of the Original Offer, with // the User's signature on it. // A copy of that original Trade object (itself with the user's // signature) is already stored in // the cron folder (by transaction number.) This happens when the // Trade is FIRST added to cron, // so it's already safe before we even get here. // // So thus I am FREE to release the signatures on the offer, and // sign with the server instead. // The server-signed offer will be stored by the OTMarket. offer_->ReleaseSignatures(); offer_->SignContract(*(GetCron()->GetServerNym())); offer_->SaveContract(); pMarket->SaveMarket(); // Now when the market loads next time, it can verify this offer // using the server's signature, // instead of having to load the user. Because the server has // verified it and added it, and now // signs it, vouching for it. // The Trade itself (all its other variables) are now allowed to // change, since its signatures // are also released and it is now server-signed. (With a copy // stored of the original.) offer_->SetTrade(*this); return offer_; } } // It's a stop order, and not activated yet. // Should we activate it now? // else if (IsStopOrder() && !stopActivated_) { int64_t relevantPrice = 0; // If the stop order is trying to sell something, then it cares about // the highest bidder. if (offer->IsAsk()) relevantPrice = pMarket->GetHighestBidPrice(); else // But if the stop order is trying to buy something, then it cares // about the lowest ask price. relevantPrice = pMarket->GetLowestAskPrice(); // It's a stop order that hasn't activated yet. SHOULD IT ACTIVATE NOW? if ((IsGreaterThan() && (relevantPrice > GetStopPrice())) || (IsLessThan() && (relevantPrice < GetStopPrice()))) { // Activate the stop order! if (!pMarket->AddOffer(this, *offer, true)) // Since we're actually // adding an offer to // the market (not just { // loading from disk) the we actually want to save the market. // Error adding the offer to the market! // bSaveFile=true. otErr << "Error adding the stop order to the market! (Even " "though supposedly the right market.)\n"; } else { // SUCCESS! offer_ = offer; stopActivated_ = true; hasTradeActivated_ = true; // The Trade (stored on Cron) has a copy of the Original Offer, // with the User's signature on it. // A copy of that original Trade object (itself with the user's // signature) is already stored in // the cron folder (by transaction number.) This happens when // the Trade is FIRST added to cron, // so it's already safe before we even get here. // // So thus I am FREE to release the signatures on the offer, and // sign with the server instead. // The server-signed offer will be stored by the OTMarket. offer_->ReleaseSignatures(); offer_->SignContract(*(GetCron()->GetServerNym())); offer_->SaveContract(); pMarket->SaveMarket(); // Now when the market loads next time, it can verify this offer // using the server's signature, // instead of having to load the user. Because the server has // verified it and added it, and now // signs it, vouching for it. // The Trade itself (all its other variables) are now allowed to // change, since its signatures // are also released and it is now server-signed. (With a copy // stored of the original.) offer_->SetTrade(*this); return offer_; } } } delete offer; offer = nullptr; return nullptr; }
// OTCron calls this regularly, which is my chance to expire, etc. // Return True if I should stay on the Cron list for more processing. // Return False if I should be removed and deleted. bool OTTrade::ProcessCron() { // Right now Cron is called 10 times per second. // I'm going to slow down all trades so they are once every // GetProcessInterval() if (GetLastProcessDate() > OT_TIME_ZERO) { // (Default ProcessInterval is 1 second, but Trades will use 10 seconds, // and Payment Plans will use an hour or day.) if (OTTimeGetTimeInterval(OTTimeGetCurrentTime(), GetLastProcessDate()) <= GetProcessInterval()) return true; } // Keep a record of the last time this was processed. // (NOT saved to storage, only used while the software is running.) // (Thus no need to release signatures, sign contract, save contract, etc.) SetLastProcessDate(OTTimeGetCurrentTime()); // PAST END DATE? // First call the parent's version (which this overrides) so it has // a chance to check its stuff. Currently it checks IsExpired(). if (!ot_super::ProcessCron()) return false; // It's expired or flagged for removal--remove it from // Cron. // You might ask, why not check here if this trade is flagged for removal? // Supposedly the answer is, because it's only below that I have the market // pointer, // and am able to remove the corresponding trade from the market. // Therefore I am adding a hook for "onRemoval" so that Objects such as // OTTrade ALWAYS // have the opportunity to perform such cleanup, without having to juggle // such logic. // REACHED START DATE? // Okay, so it's not expired. But might not have reached START DATE yet... if (!VerifyCurrentDate()) return true; // The Trade is not yet valid, so we return. BUT, we return // true, so it will stay on Cron until it BECOMES valid. // TRADE-specific stuff below. bool bStayOnMarket = true; // by default stay on the market (until some rule expires me.) Identifier OFFER_MARKET_ID; OTMarket* market = nullptr; // If the Offer is already active on a market, then I already have a pointer // to // it. This function returns that pointer. If nullptr, it tries to find the // offer on // the market and then sets the pointer and returns. If it can't find it, IT // TRIES // TO ADD IT TO THE MARKET and sets the pointer and returns it. OTOffer* offer = GetOffer( &OFFER_MARKET_ID, &market); // Both of these parameters are optional. // In this case, the offer is NOT on the market. // Perhaps it wasn't ready to activate yet. if (offer == nullptr) { // The offer SHOULD HAVE been on the market, since we're within the // valid range, // and GetOffer adds it when it's not already there. // otErr << "OTTrade::ProcessCron: Offer SHOULD have been on // Market. I might ASSERT this.\n"; // comment this out // Actually! If it's a Stop Order, then it WOULD be within the valid // range, yet would // not yet have activated. So I don't want to log some big error every // time a stop order // checks its prices. } else if (market == nullptr) { // todo. (This will already leave a log above in GetOffer somewhere.) // otErr << "OTTrade::ProcessCron: Market was nullptr.\n"; // // comment this out } else // If a valid pointer was returned, that means the offer is on the // market. { // Make sure it hasn't already been flagged by someone else... if (IsFlaggedForRemoval()) // This is checked above in // OTCronItem::ProcessCron(). bStayOnMarket = false; // I'm leaving the check here in case the // flag was set since then. else // Process it! <=================== { otInfo << "Processing trade: " << GetTransactionNum() << ".\n"; bStayOnMarket = market->ProcessTrade(*this, *offer); // No need to save the Trade or Offer, since they will // be saved inside this call if they are changed. } } // Return True if I should stay on the Cron list for more processing. // Return False if I should be removed and deleted. return bStayOnMarket; // defaults true, so if false, that means someone is // removing it for a reason. }
// OTCron calls this regularly, which is my chance to expire, etc. // Return True if I should stay on the Cron list for more processing. // Return False if I should be removed and deleted. bool OTTrade::ProcessCron() { // ----------------------------------------------------------------- // Right now Cron is called 10 times per second. // I'm going to slow down all trades so they are once every GetProcessInterval() if (GetLastProcessDate() > 0) { // (Default ProcessInternal is 1 second, but Trades will use 10 seconds, // and Payment Plans will use an hour or day.) if ((GetCurrentTime() - GetLastProcessDate()) <= GetProcessInterval()) return true; } // Keep a record of the last time this was processed. // (NOT saved to storage, only used while the software is running.) // (Thus no need to release signatures, sign contract, save contract, etc.) SetLastProcessDate(GetCurrentTime()); // ----------------------------------------------------------------- // PAST END DATE? -------------------------------- // First call the parent's version (which this overrides) so it has // a chance to check its stuff. Currently it checks IsExpired(). if (!OTCronItem::ProcessCron()) return false; // It's expired--remove it from Cron. // REACHED START DATE? -------------------------------- // Okay, so it's not expired. But might not have reached START DATE yet... if (!VerifyCurrentDate()) return true; // The Trade is not yet valid, so we return. BUT, we return // true, so it will stay on Cron until it BECOMES valid. // TRADE-specific stuff below. -------------------------------- bool bStayOnMarket = true; // by default stay on the market (until some rule expires me.) OTIdentifier OFFER_MARKET_ID; OTMarket * pMarket = NULL; // If the Offer is already active on a market, then I already have a pointer to // it. This function returns that pointer. If NULL, it tries to find the offer on // the market and then sets the pointer and returns. If it can't find it, IT TRIES // TO ADD IT TO THE MARKET and sets the pointer and returns it. OTOffer * pOffer = GetOffer(&OFFER_MARKET_ID, &pMarket); // Both of these parameters are optional. // In this case, the offer is NOT on the market. // Perhaps it wasn't ready to activate yet. if (NULL == pOffer) { // The offer SHOULD HAVE been on the market, since we're within the valid range, // and GetOffer adds it when it's not already there. // OTLog::Error("OTTrade::ProcessCron: Offer SHOULD have been on Market. I might ASSERT this.\n"); // Actually! If it's a Stop Order, then it WOULD be within the valid range, yet would // not yet have activated. So I don't want to log some big error every time a stop order // checks its prices. } else if (NULL == pMarket) { //todo. (This will already leave a log above in GetOffer somewhere.) } else // If a valid pointer was returned, that means the offer is on the market. { // Make sure it hasn't already been flagged by someone else... if (this->IsFlaggedForRemoval()) bStayOnMarket = false; else { // Process it! <=================== bStayOnMarket = pMarket->ProcessTrade(*this, *pOffer); // No need to save the Trade or Offer, since they will // be saved inside this call if they are changed. } } // The offer needs to be removed from the market. // (Which means this trade also needs to be removed from Cron. // I assume calling function will do that.) if (!bStayOnMarket) { pMarket->RemoveOffer(GetTransactionNum()); } // Return True if I should stay on the Cron list for more processing. // Return False if I should be removed and deleted. return bStayOnMarket; // defaults true, so if false, that means someone is removing it for a reason. }
// Assuming the offer is ON the market, this will get the pointer to that offer. // Otherwise it will try to add it to the market. OTOffer * OTTrade::GetOffer(OTIdentifier * pOFFER_MARKET_ID/*=NULL*/, OTMarket ** ppMarket/*=NULL*/) { OT_ASSERT(NULL != GetCron()); // See if the offer has already been instantiated. if (NULL != m_pOffer) { m_pOffer->SetTrade(*this); // Probably don't need this one. I'll remove it someday while optimizing. return m_pOffer; // In fact since it should already be set, having this here would basically // hide it from me if the memory was ever walked on from a bug somewhere. } if (!m_strOffer.Exists()) { OTLog::Error("OTTrade::GetOffer called with empty m_strOffer.\n"); return NULL; } OTOffer * pOffer = new OTOffer(); OT_ASSERT(NULL != pOffer); // -------------------------------------------------- // Trying to load the offer from the trader's original signed request // (So I can use it to lookup the Market ID, so I can see the offer is // already there on the market.) if (!pOffer->LoadContractFromString(m_strOffer)) { OTLog::Error("Error loading offer from string in OTTrade::GetOffer\n"); delete pOffer; pOffer = NULL; return NULL; } // No need to do any additional security verification here on the Offer, // since the Offer is already heavily verified in OTServer::NotarizeMarketOffer(). // So as long as you feel safe about the Trade, then you can feel safe about // the Offer already, with no further checks. // *Also remember we saved a copy of the original in the cron folder. // It loaded. Let's get the Market ID off of it so we can locate the market. OTIdentifier OFFER_MARKET_ID(*pOffer); if (NULL != pOFFER_MARKET_ID) { // Sometimes the caller function would like a copy of this ID. So I // give the option to pass in a pointer so I can give it here. pOFFER_MARKET_ID->Assign(OFFER_MARKET_ID); } // Previously if a user tried to use a market that didn't exist, I'd just return failure. // But now we will create any market that doesn't already exist. // (Remember, the server operator could just erase the market folder--it wouldn't // affect anyone's balances!) // // OTMarket * pMarket = m_pCron->GetMarket(OFFER_MARKET_ID); OTMarket * pMarket = GetCron()->GetOrCreateMarket(GetAssetID(), GetCurrencyID(), pOffer->GetScale()); // Couldn't find (or create) the market. if (NULL == pMarket) { OTLog::Output(3, "Unable to find or create market within requested parameters in OTTrade::GetOffer."); delete pOffer; pOffer = NULL; return NULL; } // If the caller passed in the address of a market pointer (optional) if (NULL != ppMarket) { // Sometimes the caller function would like a copy of this market pointer, when available. // So I pass it back to him here, if he wants. That way he doesn't have to do this work again // to look it up. *ppMarket = pMarket; } // -------------------------------------------------- // At this point, I have heap-allocated the offer, used it to get the Market ID, and successfully // used that to get a pointer to the market matching that ID. // // Let's see if the offer is ALREADY allocated and on this market! // If so, delete the one I just allocated. If not, add it to the market. OTOffer * pMarketOffer = pMarket->GetOffer(pOffer->GetTransactionNum()); // The Offer is already on the Market. // NOTE: It may just start out this way, without ever being added. // How is that possible? Because maybe it was in the market file when we first loaded up, // and had been added on some previous run of the software. So since we started running, // the pMarket->AddOffer() code below has literally never run for that offer. Instead we // first find it here, and thus return the pointer before getting any farther. // // IN ALL CASES, we make sure to call m_pOffer->SetTrade() so that it has a pointer BACK to // this Trade object! (When actually processing the offer, the market will need the account // numbers and Nym IDs... which are stored here on the trade.) if (NULL != pMarketOffer) { m_pOffer = pMarketOffer; // Since the Offer already exists on the market, no need anymore for the // one we allocated above (to get the market ID.) So we delete it now. delete pOffer; pOffer = NULL; m_pOffer->SetTrade(*this); return m_pOffer; } // Okay so the offer ISN'T already on the market. If it's not a stop order, let's ADD the one we // allocated to the market now! (Stop orders are activated through their own logic, which is in the else.) if (!IsStopOrder()) { if (m_bHasTradeActivated) { // Error -- how has the trade already activated, yet not on the market and null in my pointer? OTLog::Error("How has the trade already activated, yet not on the market and null in my pointer?\n"); } else if (!pMarket->AddOffer(*pOffer, true)) // Since we're actually adding an offer to the market (not just { // loading from disk) the we actually want to save the market. bSaveFile=true. // Error adding the offer to the market! OTLog::Error("Error adding the offer to the market! (Even though supposedly the right market.)\n"); } else { // SUCCESS! m_pOffer = pOffer; m_bHasTradeActivated = true; // The Trade (stored on Cron) has a copy of the Original Offer, with the User's signature on it. // A copy of that original Trade object (itself with the user's signature) is already stored in // the cron folder (by transaction number.) This happens when the Trade is FIRST added to cron, // so it's already safe before we even get here. // // So thus I am FREE to release the signatures on the offer, and sign with the server instead. // The server-signed offer will be stored by the OTMarket. m_pOffer->ReleaseSignatures(); m_pOffer->SignContract(*(GetCron()->GetServerNym())); m_pOffer->SaveContract(); pMarket->SaveMarket(); // Now when the market loads next time, it can verify this offer using the server's signature, // instead of having to load the user. Because the server has verified it and added it, and now // signs it, vouching for it. // The Trade itself (all its other variables) are now allowed to change, since its signatures // are also released and it is now server-signed. (With a copy stored of the original.) m_pOffer->SetTrade(*this); return m_pOffer; } } else if(IsStopOrder() && !m_bHasStopActivated) // It's a stop order. Should we activate it now? { long lRelevantPrice = 0; // If the stop order is trying to sell something, then it cares about the highest bidder. if (pOffer->IsAsk()) lRelevantPrice = pMarket->GetHighestBidPrice(); else // But if the stop order is trying to buy something, then it cares about the lowest ask price. lRelevantPrice = pMarket->GetLowestAskPrice(); // It's a stop order that hasn't activated yet. SHOULD IT ACTIVATE NOW? if ((IsGreaterThan() && (lRelevantPrice > GetStopPrice())) || (IsLessThan() && (lRelevantPrice < GetStopPrice()))) { // Activate the stop order! if (!pMarket->AddOffer(*pOffer, true)) // Since we're actually adding an offer to the market (not just { // loading from disk) the we actually want to save the market. bSaveFile=true. // Error adding the offer to the market! OTLog::Error("Error adding the stop order to the market! (Even though supposedly the right market.)\n"); } else { // SUCCESS! m_pOffer = pOffer; m_bHasStopActivated = true; m_bHasTradeActivated = true; // The Trade (stored on Cron) has a copy of the Original Offer, with the User's signature on it. // A copy of that original Trade object (itself with the user's signature) is already stored in // the cron folder (by transaction number.) This happens when the Trade is FIRST added to cron, // so it's already safe before we even get here. // // So thus I am FREE to release the signatures on the offer, and sign with the server instead. // The server-signed offer will be stored by the OTMarket. m_pOffer->ReleaseSignatures(); m_pOffer->SignContract(*(GetCron()->GetServerNym())); m_pOffer->SaveContract(); pMarket->SaveMarket(); // Now when the market loads next time, it can verify this offer using the server's signature, // instead of having to load the user. Because the server has verified it and added it, and now // signs it, vouching for it. // The Trade itself (all its other variables) are now allowed to change, since its signatures // are also released and it is now server-signed. (With a copy stored of the original.) m_pOffer->SetTrade(*this); return m_pOffer; } } } delete pOffer; pOffer = NULL; return NULL; }