Open-Transactions  0.93.0-ge03d287
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
opentxs::OTMarket Class Reference

#include <OTMarket.hpp>

Inheritance diagram for opentxs::OTMarket:
Collaboration diagram for opentxs::OTMarket:

Public Member Functions

bool ValidateOfferForMarket (OTOffer &theOffer, OTString *pReason=nullptr)
 
OTOfferGetOffer (const int64_t &lTransactionNum)
 
bool AddOffer (OTTrade *pTrade, OTOffer &theOffer, bool bSaveFile=true, time64_t tDateAddedToMarket=OT_TIME_ZERO)
 
bool RemoveOffer (const int64_t &lTransactionNum)
 
EXPORT bool GetOfferList (OTASCIIArmor &ascOutput, int64_t lDepth, int32_t &nOfferCount)
 
EXPORT bool GetRecentTradeList (OTASCIIArmor &ascOutput, int32_t &nTradeCount)
 
bool GetNym_OfferList (const OTIdentifier &NYM_ID, OTDB::OfferListNym &theOutputList, int32_t &nNymOfferCount)
 
void ProcessTrade (OTTrade &theTrade, OTOffer &theOffer, OTOffer &theOtherOffer)
 
bool ProcessTrade (OTTrade &theTrade, OTOffer &theOffer)
 
int64_t GetHighestBidPrice ()
 
int64_t GetLowestAskPrice ()
 
mapOfOffers::size_type GetBidCount ()
 
mapOfOffers::size_type GetAskCount ()
 
void SetAssetID (const OTIdentifier &ASSET_ID)
 
void SetCurrencyID (const OTIdentifier &CURRENCY_ID)
 
void SetServerID (const OTIdentifier &SERVER_ID)
 
const OTIdentifierGetAssetID () const
 
const OTIdentifierGetCurrencyID () const
 
const OTIdentifierGetServerID () const
 
const int64_t & GetScale () const
 
void SetScale (const int64_t &lScale)
 
const int64_t & GetLastSalePrice ()
 
void SetLastSalePrice (const int64_t &lLastSalePrice)
 
const std::string & GetLastSaleDate ()
 
int64_t GetTotalAvailableAssets ()
 
 OTMarket ()
 
 OTMarket (const char *szFilename)
 
 OTMarket (const OTIdentifier &SERVER_ID, const OTIdentifier &ASSET_TYPE_ID, const OTIdentifier &CURRENCY_TYPE_ID, const int64_t &lScale)
 
virtual ~OTMarket ()
 
virtual void GetIdentifier (OTIdentifier &theIdentifier) const
 
void SetCronPointer (OTCron &theCron)
 
OTCronGetCron ()
 
bool LoadMarket ()
 
bool SaveMarket ()
 
void InitMarket ()
 
virtual void Release ()
 
void Release_Market ()
 
virtual int32_t ProcessXMLNode (irr::io::IrrXMLReader *&xml)
 
virtual void UpdateContents ()
 
virtual bool SaveContractWallet (std::ofstream &ofs) const
 
- Public Member Functions inherited from opentxs::OTContract
const char * GetHashType () const
 
void SetIdentifier (const OTIdentifier &theID)
 
EXPORT OTContract ()
 
EXPORT OTContract (const OTString &name, const OTString &foldername, const OTString &filename, const OTString &strID)
 
EXPORT OTContract (const OTString &strID)
 
EXPORT OTContract (const OTIdentifier &theID)
 
void Initialize ()
 
virtual EXPORT ~OTContract ()
 
EXPORT void Release_Contract ()
 
EXPORT void ReleaseSignatures ()
 
virtual EXPORT bool CreateContract (const OTString &strContract, const OTPseudonym &theSigner)
 
EXPORT bool InsertNym (const OTString &strKeyName, const OTString &strKeyValue)
 
EXPORT void GetName (OTString &strName) const
 
EXPORT void SetName (const OTString &strName)
 
virtual EXPORT bool VerifyContract ()
 
virtual EXPORT void GetIdentifier (OTString &theIdentifier) const
 
EXPORT void GetFilename (OTString &strFilename) const
 
EXPORT void GetFoldername (OTString &strFoldername) const
 
virtual EXPORT bool LoadContract ()
 
EXPORT bool LoadContract (const char *szFoldername, const char *szFilename)
 
EXPORT bool LoadContractFromString (const OTString &theStr)
 
bool LoadContractRawFile ()
 
EXPORT bool ParseRawFile ()
 
EXPORT bool SaveToContractFolder ()
 
EXPORT bool SaveContractRaw (OTString &strOutput) const
 
EXPORT bool RewriteContract (OTString &strOutput) const
 
EXPORT bool SaveContract ()
 
EXPORT bool SaveContract (const char *szFoldername, const char *szFilename)
 
virtual EXPORT void CreateContents ()
 
EXPORT void CreateInnerContents ()
 
virtual EXPORT bool SaveContents (std::ofstream &ofs) const
 
virtual EXPORT bool SaveContractWallet (OTString &strContents) const
 
virtual EXPORT bool DisplayStatistics (OTString &strContents) const
 
virtual EXPORT bool SaveContents (OTString &strContents) const
 
virtual EXPORT bool SignContract (const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr)
 
EXPORT bool SignContractAuthent (const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr)
 
EXPORT bool SignWithKey (const OTAsymmetricKey &theKey, const OTPasswordData *pPWData=nullptr)
 
EXPORT bool SignContract (const OTPseudonym &theNym, OTSignature &theSignature, const OTPasswordData *pPWData=nullptr)
 
EXPORT bool SignContractAuthent (const OTPseudonym &theNym, OTSignature &theSignature, const OTPasswordData *pPWData=nullptr)
 
EXPORT bool SignContract (const OTAsymmetricKey &theKey, OTSignature &theSignature, const OTString &strHashType, const OTPasswordData *pPWData=nullptr)
 
EXPORT bool SignContract (const char *szFoldername, const char *szFilename, OTSignature &theSignature, const OTPasswordData *pPWData=nullptr)
 
virtual EXPORT bool VerifyContractID () const
 
virtual EXPORT void CalculateContractID (OTIdentifier &newID) const
 
virtual EXPORT bool VerifySignature (const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr) const
 
virtual EXPORT bool VerifySigAuthent (const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr) const
 
EXPORT bool VerifyWithKey (const OTAsymmetricKey &theKey, const OTPasswordData *pPWData=nullptr) const
 
EXPORT bool VerifySignature (const OTPseudonym &theNym, const OTSignature &theSignature, const OTPasswordData *pPWData=nullptr) const
 
EXPORT bool VerifySigAuthent (const OTPseudonym &theNym, const OTSignature &theSignature, const OTPasswordData *pPWData=nullptr) const
 
EXPORT bool VerifySignature (const OTAsymmetricKey &theKey, const OTSignature &theSignature, const OTString &strHashType, const OTPasswordData *pPWData=nullptr) const
 
EXPORT bool VerifySignature (const char *szFoldername, const char *szFilename, const OTSignature &theSignature, const OTPasswordData *pPWData=nullptr) const
 
EXPORT const OTAsymmetricKeyGetContractPublicKey () const
 
EXPORT const OTPseudonymGetContractPublicNym () const
 

Additional Inherited Members

- Static Public Member Functions inherited from opentxs::OTContract
static EXPORT bool DearmorAndTrim (const OTString &strInput, OTString &strOutput, OTString &strFirstLine)
 
static bool AddBookendsAroundContent (OTString &strOutput, const OTString &strContents, const OTString &strContractType, const OTString &strHashType, const listOfSignatures &listSignatures)
 
static EXPORT bool LoadEncodedTextField (irr::io::IrrXMLReader *&xml, OTASCIIArmor &ascOutput)
 
static EXPORT bool LoadEncodedTextField (irr::io::IrrXMLReader *&xml, OTString &strOutput)
 
static bool LoadEncodedTextFieldByName (irr::io::IrrXMLReader *&xml, OTASCIIArmor &ascOutput, const char *&szName, OTString::Map *pmapExtraVars=nullptr)
 
static bool LoadEncodedTextFieldByName (irr::io::IrrXMLReader *&xml, OTString &strOutput, const char *&szName, OTString::Map *pmapExtraVars=nullptr)
 
static bool SkipToElement (irr::io::IrrXMLReader *&xml)
 
static bool SkipToTextField (irr::io::IrrXMLReader *&xml)
 
static bool SkipAfterLoadingField (irr::io::IrrXMLReader *&xml)
 
static EXPORT bool SignFlatText (OTString &strFlatText, const OTString &strContractType, const OTPseudonym &theSigner, OTString &strOutput)
 
- Protected Member Functions inherited from opentxs::OTContract
bool LoadContractXML ()
 
- Protected Attributes inherited from opentxs::OTContract
OTString m_strName
 
OTString m_strFoldername
 
OTString m_strFilename
 
OTIdentifier m_ID
 
OTStringXML m_xmlUnsigned
 
OTString m_strRawFile
 
OTString m_strSigHashType
 
OTString m_strContractType
 
mapOfNyms m_mapNyms
 
listOfSignatures m_listSignatures
 
OTString m_strVersion
 
OTString m_strEntityShortName
 
OTString m_strEntityLongName
 
OTString m_strEntityEmail
 
OTString::Map m_mapConditions
 

Detailed Description

Definition at line 161 of file OTMarket.hpp.

Constructor & Destructor Documentation

opentxs::OTMarket::OTMarket ( )

Definition at line 2747 of file OTMarket.cpp.

2748  : OTContract()
2749  , m_pCron(nullptr)
2750  , m_pTradeList(nullptr)
2751  , m_lScale(1)
2752  , m_lLastSalePrice(0)
2753 {
2754  m_pCron = nullptr; // just for convenience, not responsible to delete.
2755  InitMarket();
2756 }
opentxs::OTMarket::OTMarket ( const char *  szFilename)

Definition at line 2731 of file OTMarket.cpp.

2732  : OTContract()
2733  , m_pCron(nullptr)
2734  , m_pTradeList(nullptr)
2735  , m_lScale(1)
2736  , m_lLastSalePrice(0)
2737 {
2738  OT_ASSERT(nullptr != szFilename);
2739 
2740  m_pCron = nullptr; // just for convenience, not responsible to delete.
2741  InitMarket();
2742 
2743  m_strFilename.Set(szFilename);
2745 }
EXPORT void Set(const char *data, uint32_t enforcedMaxLength=0)
Definition: OTString.cpp:1055
#define OT_ASSERT(x)
Definition: Assert.hpp:150
static EXPORT const OTString & Market()
Definition: OTFolders.cpp:319
OTString m_strFoldername
Definition: OTContract.hpp:169
opentxs::OTMarket::OTMarket ( const OTIdentifier SERVER_ID,
const OTIdentifier ASSET_TYPE_ID,
const OTIdentifier CURRENCY_TYPE_ID,
const int64_t &  lScale 
)

Definition at line 2758 of file OTMarket.cpp.

2761  : OTContract()
2762  , m_pCron(nullptr)
2763  , m_pTradeList(nullptr)
2764  , m_lScale(1)
2765  , m_lLastSalePrice(0)
2766 {
2767  m_pCron = nullptr; // just for convenience, not responsible to delete.
2768  InitMarket();
2769 
2770  m_ASSET_TYPE_ID = ASSET_TYPE_ID;
2771  m_CURRENCY_TYPE_ID = CURRENCY_TYPE_ID;
2772 
2773  m_SERVER_ID = SERVER_ID;
2774 
2775  SetScale(lScale);
2776 }
void SetScale(const int64_t &lScale)
Definition: OTMarket.hpp:281
opentxs::OTMarket::~OTMarket ( )
virtual

Definition at line 2778 of file OTMarket.cpp.

2779 {
2780  Release_Market();
2781 }

Member Function Documentation

bool opentxs::OTMarket::AddOffer ( OTTrade pTrade,
OTOffer theOffer,
bool  bSaveFile = true,
time64_t  tDateAddedToMarket = OT_TIME_ZERO 
)

Definition at line 740 of file OTMarket.cpp.

742 {
743  const int64_t lTransactionNum = theOffer.GetTransactionNum(),
744  lPriceLimit = theOffer.GetPriceLimit();
745 
746  // Make sure the offer is even appropriate for this market...
747  if (!ValidateOfferForMarket(theOffer)) {
748  otErr << "Failed attempt to add invalid offer to market.\n";
749 
750  if (nullptr != pTrade) pTrade->FlagForRemoval();
751  }
752  else {
753  // I store duplicate lists of offer pointers. Two multimaps ordered by
754  // price,
755  // (for buyers and sellers) and one map ordered by transaction number.
756 
757  // See if there's something else already there with the same transaction
758  // number.
759  //
760  auto it = m_mapOffers.find(lTransactionNum);
761 
762  // If it's not already on the list, then add it...
763  if (it == m_mapOffers.end()) {
764  m_mapOffers[lTransactionNum] = &theOffer;
765  otLog4 << "Offer added as an offer to the market...\n";
766  }
767  // Otherwise, if it was already there, log an error.
768  else {
769  otErr << "Attempt to add Offer to Market with pre-existing "
770  "transaction number: " << lTransactionNum << "\n";
771  return false;
772  }
773 
774  // Okay so we successfully added it to one list (indexed by Transaction
775  // Num) and we
776  // know it validated as an offer, AND we know it wasn't already on the
777  // market.
778  //
779  // So next, let's add it to the lists that are indexed by price:
780 
781  // Determine if it's a buy or sell, and add it to the right list.
782  if (theOffer.IsBid()) {
783  // No bother checking if the offer is already on this list,
784  // since the code above basically already verifies that for us.
785 
786  m_mapBids.insert(
787  m_mapBids.lower_bound(lPriceLimit), // highest bidders go first,
788  // so I am last in line at
789  // lower bound.
790  std::pair<int64_t, OTOffer*>(lPriceLimit, &theOffer));
791  otLog4 << "Offer added as a bid to the market.\n";
792  }
793  else {
794  m_mapAsks.insert(
795  m_mapAsks.upper_bound(lPriceLimit), // lowest price sells first,
796  // so I am last in line at
797  // upper bound.
798  std::pair<int64_t, OTOffer*>(lPriceLimit, &theOffer));
799  otLog4 << "Offer added as an ask to the market.\n";
800  }
801 
802  if (bSaveFile) {
803  // Set this to the current date/time, since the offer is
804  // being added for the first time.
805  //
806  theOffer.SetDateAddedToMarket(OTTimeGetCurrentTime());
807 
808  return SaveMarket(); // <====== SAVE since an offer was added to the
809  // Market.
810  }
811  else {
812  // Set this to the date passed in, since this offer was
813  // added to the market in the past, and we are preserving that date.
814  //
815  theOffer.SetDateAddedToMarket(tDateAddedToMarket);
816 
817  return true;
818  }
819  }
820 
821  return false;
822 }
OTLOG_IMPORT OTLogStream otLog4
time64_t OTTimeGetCurrentTime()
Definition: Common.hpp:211
bool ValidateOfferForMarket(OTOffer &theOffer, OTString *pReason=nullptr)
Definition: OTMarket.cpp:2660
OTLOG_IMPORT OTLogStream otErr
mapOfOffers::size_type opentxs::OTMarket::GetAskCount ( )
inline

Definition at line 247 of file OTMarket.hpp.

248  {
249  return m_mapAsks.size();
250  }
const OTIdentifier& opentxs::OTMarket::GetAssetID ( ) const
inline

Definition at line 264 of file OTMarket.hpp.

265  {
266  return m_ASSET_TYPE_ID;
267  }
mapOfOffers::size_type opentxs::OTMarket::GetBidCount ( )
inline

Definition at line 243 of file OTMarket.hpp.

244  {
245  return m_mapBids.size();
246  }
OTCron* opentxs::OTMarket::GetCron ( )
inline

Definition at line 317 of file OTMarket.hpp.

318  {
319  return m_pCron;
320  }
const OTIdentifier& opentxs::OTMarket::GetCurrencyID ( ) const
inline

Definition at line 268 of file OTMarket.hpp.

269  {
270  return m_CURRENCY_TYPE_ID;
271  }
int64_t opentxs::OTMarket::GetHighestBidPrice ( )

Definition at line 919 of file OTMarket.cpp.

920 {
921  int64_t lPrice = 0;
922 
923  mapOfOffers::reverse_iterator rr = m_mapBids.rbegin();
924 
925  if (rr != m_mapBids.rend()) {
926  lPrice = rr->first;
927  }
928 
929  return lPrice;
930 }
void opentxs::OTMarket::GetIdentifier ( OTIdentifier theIdentifier) const
virtual

Reimplemented from opentxs::OTContract.

Definition at line 903 of file OTMarket.cpp.

904 {
905  OTString strTemp, strAsset(GetAssetID()), strCurrency(GetCurrencyID());
906 
907  int64_t lScale = GetScale();
908 
909  // In this way we generate a unique ID that will always be consistent
910  // for the same asset ID, currency ID, and market scale.
911  strTemp.Format("ASSET TYPE:\n%s\nCURRENCY TYPE:\n%s\nMARKET SCALE:\n%lld\n",
912  strAsset.Get(), strCurrency.Get(), lScale);
913 
914  theIdentifier.CalculateDigest(strTemp);
915 }
const OTIdentifier & GetCurrencyID() const
Definition: OTMarket.hpp:268
const OTIdentifier & GetAssetID() const
Definition: OTMarket.hpp:264
const int64_t & GetScale() const
Definition: OTMarket.hpp:277
const std::string& opentxs::OTMarket::GetLastSaleDate ( )
inline

Definition at line 298 of file OTMarket.hpp.

299  {
300  return m_strLastSaleDate;
301  }
const int64_t& opentxs::OTMarket::GetLastSalePrice ( )
inline

Definition at line 287 of file OTMarket.hpp.

288  {
289  if (m_lLastSalePrice < 1) m_lLastSalePrice = 1;
290  return m_lLastSalePrice;
291  }
int64_t opentxs::OTMarket::GetLowestAskPrice ( )

Definition at line 934 of file OTMarket.cpp.

935 {
936  int64_t lPrice = 0;
937 
938  auto it = m_mapAsks.begin();
939 
940  if (it != m_mapAsks.end()) {
941  lPrice = it->first;
942 
943  // Market orders have a 0 price, so we need to skip any if they are
944  // here.
945  //
946  // Note that we don't have to do this with the highest bid price (above
947  // function) but in the case of asks, a "0 price" will undercut the
948  // other
949  // actual prices, so we need to skip any that have a 0 price.
950  //
951  while (0 == lPrice) {
952  ++it;
953 
954  if (it == m_mapAsks.end()) break;
955 
956  lPrice = it->first;
957  }
958  }
959 
960  return lPrice;
961 }
bool opentxs::OTMarket::GetNym_OfferList ( const OTIdentifier NYM_ID,
OTDB::OfferListNym theOutputList,
int32_t &  nNymOfferCount 
)

Definition at line 310 of file OTMarket.cpp.

313 {
314  nNymOfferCount =
315  0; // Outputs the count of offers for NYM_ID (on this market.)
316 
317  // Loop through the offers, up to some maximum depth, and then add each
318  // as a data member to an offer list, then pack it into ascOutput.
319  //
320  for (auto& it : m_mapOffers) {
321  OTOffer* pOffer = it.second;
322  OT_ASSERT(nullptr != pOffer);
323 
324  OTTrade* pTrade = pOffer->GetTrade();
325 
326  // We only return offers for a specific Nym ID, since this is private
327  // info only for that Nym.
328  //
329  if ((nullptr == pTrade) || (pTrade->GetSenderUserID() != NYM_ID))
330  continue;
331 
332  // Below this point, I KNOW pTrade and pOffer are both good pointers.
333  // with no need to cleanup. I also know they are for the right Nym.
334 
335  std::unique_ptr<OTDB::OfferDataNym> pOfferData(
336  dynamic_cast<OTDB::OfferDataNym*>(
338 
339  const int64_t& lTransactionNum = pOffer->GetTransactionNum();
340  const int64_t& lPriceLimit = pOffer->GetPriceLimit();
341  const int64_t& lTotalAssets = pOffer->GetTotalAssetsOnOffer();
342  const int64_t& lFinishedSoFar = pOffer->GetFinishedSoFar();
343  const int64_t& lMinimumIncrement = pOffer->GetMinimumIncrement();
344  const int64_t& lScale = pOffer->GetScale();
345 
346  const time64_t tValidFrom = pOffer->GetValidFrom();
347  const time64_t tValidTo = pOffer->GetValidTo();
348 
349  const time64_t tDateAddedToMarket = pOffer->GetDateAddedToMarket();
350 
351  const OTIdentifier& theServerID = pOffer->GetServerID();
352  const OTString strServerID(theServerID);
353  const OTIdentifier& theAssetID = pOffer->GetAssetID();
354  const OTString strAssetID(theAssetID);
355  const OTIdentifier& theAssetAcctID = pTrade->GetSenderAcctID();
356  const OTString strAssetAcctID(theAssetAcctID);
357  const OTIdentifier& theCurrencyID = pOffer->GetCurrencyID();
358  const OTString strCurrencyID(theCurrencyID);
359  const OTIdentifier& theCurrencyAcctID = pTrade->GetCurrencyAcctID();
360  const OTString strCurrencyAcctID(theCurrencyAcctID);
361 
362  const bool bSelling = pOffer->IsAsk();
363 
364  if (pTrade->IsStopOrder()) {
365  if (pTrade->IsGreaterThan())
366  pOfferData->stop_sign = ">";
367  else if (pTrade->IsLessThan())
368  pOfferData->stop_sign = "<";
369 
370  if (!pOfferData->stop_sign.compare(">") ||
371  !pOfferData->stop_sign.compare("<")) {
372  const int64_t& lStopPrice = pTrade->GetStopPrice();
373  pOfferData->stop_price = to_string<int64_t>(lStopPrice);
374  }
375  }
376 
377  pOfferData->transaction_id = to_string<int64_t>(lTransactionNum);
378  pOfferData->price_per_scale = to_string<int64_t>(lPriceLimit);
379  pOfferData->total_assets = to_string<int64_t>(lTotalAssets);
380  pOfferData->finished_so_far = to_string<int64_t>(lFinishedSoFar);
381  pOfferData->minimum_increment = to_string<int64_t>(lMinimumIncrement);
382  pOfferData->scale = to_string<int64_t>(lScale);
383 
384  pOfferData->valid_from = to_string<time64_t>(tValidFrom);
385  pOfferData->valid_to = to_string<time64_t>(tValidTo);
386 
387  pOfferData->date = to_string<time64_t>(tDateAddedToMarket);
388 
389  pOfferData->server_id = strServerID.Get();
390  pOfferData->asset_type_id = strAssetID.Get();
391  pOfferData->asset_acct_id = strAssetAcctID.Get();
392  pOfferData->currency_type_id = strCurrencyID.Get();
393  pOfferData->currency_acct_id = strCurrencyAcctID.Get();
394 
395  pOfferData->selling = bSelling;
396 
397  // *pOfferData is CLONED at this time (I'm still responsible to delete.)
398  // That's also why I add it here, below: So the data is set right before
399  // the cloning occurs.
400  //
401  theOutputList.AddOfferDataNym(*pOfferData);
402  nNymOfferCount++;
403  }
404 
405  return true;
406 }
EXPORT Storable * CreateObject(StoredObjectType eType)
Definition: OTStorage.cpp:530
int64_t time64_t
Definition: Common.hpp:209
#define OT_ASSERT(x)
Definition: Assert.hpp:150
OTOffer * opentxs::OTMarket::GetOffer ( const int64_t &  lTransactionNum)

Definition at line 623 of file OTMarket.cpp.

624 {
625  // See if there's something there with that transaction number.
626  auto it = m_mapOffers.find(lTransactionNum);
627 
628  if (it == m_mapOffers.end()) {
629  // nothing found.
630  return nullptr;
631  }
632  // Found it!
633  else {
634  OTOffer* pOffer = it->second;
635 
636  OT_ASSERT((nullptr != pOffer));
637 
638  if (pOffer->GetTransactionNum() == lTransactionNum)
639  return pOffer;
640  else
641  otErr << "Expected Offer with transaction number "
642  << lTransactionNum << ", but found "
643  << pOffer->GetTransactionNum() << " inside. Bad data?\n";
644  }
645 
646  return nullptr;
647 }
#define OT_ASSERT(x)
Definition: Assert.hpp:150
OTLOG_IMPORT OTLogStream otErr
bool opentxs::OTMarket::GetOfferList ( OTASCIIArmor ascOutput,
int64_t  lDepth,
int32_t &  nOfferCount 
)

Definition at line 478 of file OTMarket.cpp.

480 {
481  nOfferCount = 0; // Outputs the actual count of offers being returned.
482 
483  if (0 == lDepth) lDepth = MAX_MARKET_QUERY_DEPTH;
484 
485  // Loop through the offers, up to some maximum depth, and then add each
486  // as a data member to an offer list, then pack it into ascOutput.
487 
488  std::unique_ptr<OTDB::OfferListMarket> pOfferList(
489  dynamic_cast<OTDB::OfferListMarket*>(
491 
492  // mapOfOffers m_mapBids; // The buyers, ordered by
493  // price limit
494  // mapOfOffers m_mapAsks; // The sellers, ordered by
495  // price limit
496 
497  int32_t nTempDepth = 0;
498 
499  for (auto& it : m_mapBids) {
500  if (nTempDepth++ > lDepth) break;
501 
502  OTOffer* pOffer = it.second;
503  OT_ASSERT(nullptr != pOffer);
504 
505  const int64_t& lPriceLimit = pOffer->GetPriceLimit();
506 
507  if (0 == lPriceLimit) // Skipping any market orders.
508  continue;
509 
510  // OfferDataMarket
511  std::unique_ptr<OTDB::BidData> pOfferData(dynamic_cast<OTDB::BidData*>(
513 
514  const int64_t& lTransactionNum = pOffer->GetTransactionNum();
515  const int64_t lAvailableAssets = pOffer->GetAmountAvailable();
516  const int64_t& lMinimumIncrement = pOffer->GetMinimumIncrement();
517  const time64_t tDateAddedToMarket = pOffer->GetDateAddedToMarket();
518 
519  pOfferData->transaction_id = to_string<int64_t>(lTransactionNum);
520  pOfferData->price_per_scale = to_string<int64_t>(lPriceLimit);
521  pOfferData->available_assets = to_string<int64_t>(lAvailableAssets);
522  pOfferData->minimum_increment = to_string<int64_t>(lMinimumIncrement);
523  pOfferData->date = to_string<time64_t>(tDateAddedToMarket);
524 
525  // *pOfferData is CLONED at this time (I'm still responsible to delete.)
526  // That's also why I add it here, below: So the data is set right before
527  // the cloning occurs.
528  //
529  pOfferList->AddBidData(*pOfferData);
530  nOfferCount++;
531  }
532 
533  nTempDepth = 0;
534 
535  for (auto& it : m_mapAsks) {
536  if (nTempDepth++ > lDepth) break;
537 
538  OTOffer* pOffer = it.second;
539  OT_ASSERT(nullptr != pOffer);
540 
541  // OfferDataMarket
542  std::unique_ptr<OTDB::AskData> pOfferData(dynamic_cast<OTDB::AskData*>(
544 
545  const int64_t& lTransactionNum = pOffer->GetTransactionNum();
546  const int64_t& lPriceLimit = pOffer->GetPriceLimit();
547  const int64_t lAvailableAssets = pOffer->GetAmountAvailable();
548  const int64_t& lMinimumIncrement = pOffer->GetMinimumIncrement();
549  const time64_t tDateAddedToMarket = pOffer->GetDateAddedToMarket();
550 
551  pOfferData->transaction_id = to_string<int64_t>(lTransactionNum);
552  pOfferData->price_per_scale = to_string<int64_t>(lPriceLimit);
553  pOfferData->available_assets = to_string<int64_t>(lAvailableAssets);
554  pOfferData->minimum_increment = to_string<int64_t>(lMinimumIncrement);
555  pOfferData->date = to_string<time64_t>(tDateAddedToMarket);
556 
557  // *pOfferData is CLONED at this time (I'm still responsible to delete.)
558  // That's also why I add it here, below: So the data is set right before
559  // the cloning occurs.
560  //
561  pOfferList->AddAskData(*pOfferData);
562  nOfferCount++;
563  }
564 
565  // Now pack the list into strOutput...
566 
567  if (nOfferCount == 0)
568  return true; // Success, but there were zero offers found.
569 
570  if (nOfferCount > 0) {
571  OTDB::Storage* pStorage = OTDB::GetDefaultStorage();
572  OT_ASSERT(nullptr != pStorage);
573 
574  OTDB::OTPacker* pPacker =
575  pStorage->GetPacker(); // No need to check for failure, since this
576  // already ASSERTS. No need to cleanup
577  // either.
578 
579  std::unique_ptr<OTDB::PackedBuffer> pBuffer(
580  pPacker->Pack(*pOfferList)); // Now we PACK our market's offer list.
581 
582  if (nullptr == pBuffer) {
583  otErr << "Failed packing pOfferList in OTCron::GetOfferList. \n";
584  return false;
585  }
586 
587  // Now we need to translate pBuffer into strOutput.
588 
589  const uint8_t* pUint = static_cast<const uint8_t*>(pBuffer->GetData());
590  const size_t theSize = pBuffer->GetSize();
591 
592  if (nullptr != pUint) {
593  OTData theData(pUint, static_cast<uint32_t>(theSize));
594 
595  // This function will base64 ENCODE theData,
596  // and then Set() that as the string contents.
597  ascOutput.SetData(theData);
598 
599  return true;
600  }
601  else
602  otErr << "Error while getting buffer data in "
603  "OTMarket::GetOfferList.\n";
604  }
605  else
606  otErr << "invalid: nOfferCount is < 0 in OTMarket::GetOfferList.\n";
607 
608  return false;
609 }
EXPORT Storage * GetDefaultStorage()
Definition: OTStorage.cpp:480
EXPORT Storable * CreateObject(StoredObjectType eType)
Definition: OTStorage.cpp:530
int64_t time64_t
Definition: Common.hpp:209
#define OT_ASSERT(x)
Definition: Assert.hpp:150
OTLOG_IMPORT OTLogStream otErr
#define MAX_MARKET_QUERY_DEPTH
Definition: OTMarket.hpp:149
bool opentxs::OTMarket::GetRecentTradeList ( OTASCIIArmor ascOutput,
int32_t &  nTradeCount 
)

Definition at line 408 of file OTMarket.cpp.

409 {
410  nTradeCount = 0; // Output the count of trades in the list being returned.
411  // (If success..)
412 
413  if (nullptr == m_pTradeList) {
414  // otErr << "OTMarket::GetRecentTradeList: m_pTradeList is nullptr.
415  // \n";
416  return true;
417  // Returning true, since it's normal for this to be nullptr when the
418  // list
419  // is empty.
420  }
421 
422  // The market already keeps a list of recent trades (informational only)
423  //
424 
425  const size_t sizeList = m_pTradeList->GetTradeDataMarketCount();
426  nTradeCount = static_cast<int32_t>(sizeList);
427 
428  if (nTradeCount == 0)
429  return true; // Success, but there are 0 trade datas to return. (empty
430  // list.)
431 
432  // So now, let's pack the list into strOutput...
433  else if (nTradeCount > 0) {
434  OTDB::Storage* pStorage = OTDB::GetDefaultStorage();
435  OT_ASSERT(nullptr != pStorage);
436 
437  OTDB::OTPacker* pPacker =
438  pStorage->GetPacker(); // No need to check for failure, since this
439  // already ASSERTS. No need to cleanup
440  // either.
441 
442  std::unique_ptr<OTDB::PackedBuffer> pBuffer(pPacker->Pack(
443  *m_pTradeList)); // Now we PACK our market's recent trades list.
444 
445  if (nullptr == pBuffer) {
446  otErr << "Failed packing pTradeList in OTCron::GetRecentTradeList. "
447  "\n";
448  return false;
449  }
450 
451  // Now we need to translate pBuffer into strOutput.
452 
453  const uint8_t* pUint = static_cast<const uint8_t*>(pBuffer->GetData());
454  const size_t theSize = pBuffer->GetSize();
455 
456  if ((nullptr != pUint) || (theSize < 2)) {
457  OTData theData(pUint, static_cast<uint32_t>(theSize));
458 
459  // This function will base64 ENCODE theData,
460  // and then Set() that as the string contents.
461  ascOutput.SetData(theData);
462 
463  return true;
464  }
465  else
466  otErr << "Error while getting buffer data in "
467  "OTMarket::GetRecentTradeList.\n";
468  }
469  else
470  otErr << "Error: nTradeCount with negative value in "
471  "OTMarket::GetRecentTradeList: " << nTradeCount << ".\n";
472 
473  return false;
474 }
EXPORT Storage * GetDefaultStorage()
Definition: OTStorage.cpp:480
#define OT_ASSERT(x)
Definition: Assert.hpp:150
OTLOG_IMPORT OTLogStream otErr
const int64_t& opentxs::OTMarket::GetScale ( ) const
inline

Definition at line 277 of file OTMarket.hpp.

278  {
279  return m_lScale;
280  }
const OTIdentifier& opentxs::OTMarket::GetServerID ( ) const
inline

Definition at line 272 of file OTMarket.hpp.

273  {
274  return m_SERVER_ID;
275  }
int64_t opentxs::OTMarket::GetTotalAvailableAssets ( )

Definition at line 294 of file OTMarket.cpp.

295 {
296  int64_t lTotal = 0;
297 
298  for (auto& it : m_mapAsks) {
299  OTOffer* pOffer = it.second;
300  OT_ASSERT(nullptr != pOffer);
301 
302  lTotal += pOffer->GetAmountAvailable();
303  }
304 
305  return lTotal;
306 }
#define OT_ASSERT(x)
Definition: Assert.hpp:150
void opentxs::OTMarket::InitMarket ( )

Definition at line 2783 of file OTMarket.cpp.

2784 {
2785  m_strContractType = "MARKET";
2786 
2787  SetScale(1);
2788 }
OTString m_strContractType
Definition: OTContract.hpp:178
void SetScale(const int64_t &lScale)
Definition: OTMarket.hpp:281
bool opentxs::OTMarket::LoadMarket ( )

Definition at line 824 of file OTMarket.cpp.

825 {
826  OT_ASSERT(nullptr != GetCron());
827  OT_ASSERT(nullptr != GetCron()->GetServerNym());
828 
829  OTIdentifier MARKET_ID(*this);
830  OTString str_MARKET_ID(MARKET_ID);
831 
832  const char* szFoldername = OTFolders::Market().Get();
833  const char* szFilename = str_MARKET_ID.Get();
834 
835  bool bSuccess = OTDB::Exists(szFoldername, szFilename);
836 
837  if (bSuccess) bSuccess = LoadContract(szFoldername, szFilename); // todo ??
838 
839  if (bSuccess) bSuccess = VerifySignature(*(GetCron()->GetServerNym()));
840 
841  // Load the list of recent market trades (informational only.)
842  //
843  if (bSuccess) {
844  if (nullptr != m_pTradeList) delete m_pTradeList;
845 
846  const char* szSubFolder = "recent"; // todo stop hardcoding.
847 
848  m_pTradeList = dynamic_cast<OTDB::TradeListMarket*>(OTDB::QueryObject(
849  OTDB::STORED_OBJ_TRADE_LIST_MARKET, szFoldername, // markets
850  szSubFolder, // markets/recent
851  szFilename)); // markets/recent/market_ID
852  }
853 
854  return bSuccess;
855 }
EXPORT Storable * QueryObject(StoredObjectType theObjectType, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:788
#define OT_ASSERT(x)
Definition: Assert.hpp:150
virtual EXPORT bool LoadContract()
static EXPORT const OTString & Market()
Definition: OTFolders.cpp:319
EXPORT const char * Get() const
Definition: OTString.cpp:1045
virtual EXPORT bool VerifySignature(const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr) const
Definition: OTContract.cpp:818
EXPORT bool Exists(std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:584
OTCron * GetCron()
Definition: OTMarket.hpp:317
void opentxs::OTMarket::ProcessTrade ( OTTrade theTrade,
OTOffer theOffer,
OTOffer theOtherOffer 
)

Definition at line 1023 of file OTMarket.cpp.

1025 {
1026  OTTrade* pOtherTrade = theOtherOffer.GetTrade();
1027  OTCron* pCron = theTrade.GetCron();
1028 
1029  // Make sure have pointer to both trades.
1030  //
1031  OT_ASSERT_MSG(nullptr != pOtherTrade,
1032  "Offer was on the market, but somehow "
1033  "got into processing without a trade "
1034  "pointer.\n");
1035  OT_ASSERT(nullptr !=
1036  pCron); // Also need the Cron pointer which SHOULD ALWAYS
1037  // be there.
1038 
1039  OTPseudonym* pServerNym = pCron->GetServerNym();
1040 
1041  OT_ASSERT_MSG(nullptr != pServerNym,
1042  "Somehow a Market is running even though "
1043  "there is no Server Nym on the Cron "
1044  "object authorizing the trades.");
1045 
1046  const OTIdentifier SERVER_ID(pCron->GetServerID());
1047 
1048  if (pCron->GetTransactionCount() < 1) {
1049  otOut << "Failed to process trades: Out of transaction numbers!\n";
1050  return;
1051  }
1052 
1053  // Make sure these two separate trades don't have the same Account IDs
1054  // inside
1055  // otherwise we would have to take care not to load them twice, like with
1056  // the Nyms below.
1057  // (Instead I just disallow the trade entirely.)
1058  //
1059  if ((theTrade.GetSenderAcctID() == pOtherTrade->GetSenderAcctID()) ||
1060  (theTrade.GetSenderAcctID() == pOtherTrade->GetCurrencyAcctID()) ||
1061  (theTrade.GetCurrencyAcctID() == pOtherTrade->GetSenderAcctID()) ||
1062  (theTrade.GetCurrencyAcctID() == pOtherTrade->GetCurrencyAcctID())) {
1063  otLog5 << "Failed to process trades: they had account IDs in common.\n";
1064 
1065  // No need to remove either of the trades since they might still be
1066  // valid when
1067  // matched up to others.
1068  // I put the log on the most verbose level because if this happens, it
1069  // will probably
1070  // happen over and over again. (Unless the market has enough depth to
1071  // process them
1072  // both out.)
1073 
1074  // TODO May need to choose (or add a config option) so server operators
1075  // can remove the
1076  // oldest or newest trade when this happens, or both. Or it can choose
1077  // to do nothing,
1078  // and let them both process out with other trades (as this does now.)
1079 
1080  return;
1081  }
1082  // No need to compare asset types, since those are already verified by the
1083  // time
1084  // we get here. BUT, when the accounts are actually loaded up, THEN should
1085  // compare
1086  // the asset types to make sure they were what we expected them to be.
1087 
1088  // -------------- Make sure have both nyms loaded and checked out.
1089  // --------------------------------------------------
1090  // WARNING: 1 or both of the Nyms could be also the Server Nym. They could
1091  // also be the same Nym, but NOT the Server.
1092  // In all of those different cases, I don't want to load the same file twice
1093  // and overwrite it with itself, losing
1094  // half of all my changes. I have to check all three IDs carefully and set
1095  // the pointers accordingly, and then operate
1096  // using the pointers from there.
1097 
1098  const OTIdentifier FIRST_NYM_ID(
1099  theTrade.GetSenderUserID()), // The newest trade's Nym.
1100  OTHER_NYM_ID(pOtherTrade->GetSenderUserID()), // The Nym of the trade
1101  // that was already on the
1102  // market. (Could be same
1103  // Nym.)
1104  SERVER_NYM_ID(
1105  *pServerNym); // The Server Nym (could be one or both of the above.)
1106 
1107  OTPseudonym theNym,
1108  theOtherNym; // We MIGHT use ONE, OR BOTH, of these, or none.
1109 
1110  // Find out if either Nym is actually also the server.
1111  bool bFirstNymIsServerNym =
1112  ((FIRST_NYM_ID == SERVER_NYM_ID) ? true : false);
1113  bool bOtherNymIsServerNym =
1114  ((OTHER_NYM_ID == SERVER_NYM_ID) ? true : false);
1115 
1116  // We also see, after all that is done, whether both pointers go to the same
1117  // entity. We'll want to know that later.
1118  bool bTradersAreSameNym = ((FIRST_NYM_ID == OTHER_NYM_ID) ? true : false);
1119 
1120  // Initially both nym pointers are set to their own blank objects
1121  OTPseudonym* pFirstNym = nullptr;
1122  OTPseudonym* pOtherNym = nullptr;
1123 
1124  // Unless either of them is actually the server,
1125  // in which case the pointer is re-pointed to the server Nym.
1126  if (bFirstNymIsServerNym) // If the First Nym is the server, then just point
1127  // to that.
1128  {
1129  pFirstNym = pServerNym;
1130  }
1131  else // Else load the First Nym from storage.
1132  {
1133  theNym.SetIdentifier(FIRST_NYM_ID); // theNym is pFirstNym
1134 
1135  if (!theNym.LoadPublicKey()) {
1136  OTString strNymID(FIRST_NYM_ID);
1137  otErr << "Failure loading First Nym public key in OTMarket::"
1138  << __FUNCTION__ << ": " << strNymID << "\n";
1139  theTrade.FlagForRemoval();
1140  return;
1141  }
1142 
1143  if (theNym.VerifyPseudonym() && theTrade.VerifySignature(*pServerNym) &&
1144  theOffer.VerifySignature(*pServerNym) &&
1145  theNym.LoadSignedNymfile(*pServerNym)) // ServerNym here is not
1146  // theNym's identity, but
1147  // merely the signer on this
1148  // file.
1149  {
1150  pFirstNym = &theNym; // <=====
1151  }
1152  else {
1153  OTString strNymID(FIRST_NYM_ID);
1154  otErr << "OTMarket::" << __FUNCTION__
1155  << ": Failure verifying trade, offer, or nym, or loading "
1156  "signed Nymfile: " << strNymID << "\n";
1157  theTrade.FlagForRemoval();
1158  return;
1159  }
1160  }
1161 
1162  if (bOtherNymIsServerNym) // If the Other Nym is the server, then just point
1163  // to that.
1164  {
1165  pOtherNym = pServerNym;
1166  }
1167  else if (bTradersAreSameNym) // Else if the Traders are the same Nym,
1168  // point to the one we already loaded.
1169  {
1170  pOtherNym = pFirstNym; // theNym is pFirstNym
1171  }
1172  else // Otherwise load the Other Nym from Disk and point to that.
1173  {
1174  theOtherNym.SetIdentifier(OTHER_NYM_ID);
1175 
1176  if (!theOtherNym.LoadPublicKey()) {
1177  OTString strNymID(OTHER_NYM_ID);
1178  otErr << "Failure loading Other Nym public key in OTMarket::"
1179  << __FUNCTION__ << ": " << strNymID << "\n";
1180  pOtherTrade->FlagForRemoval();
1181  return;
1182  }
1183 
1184  if (theOtherNym.VerifyPseudonym() &&
1185  pOtherTrade->VerifySignature(*pServerNym) &&
1186  theOtherOffer.VerifySignature(*pServerNym) &&
1187  theOtherNym.LoadSignedNymfile(*pServerNym)) {
1188  pOtherNym = &theOtherNym; // <=====
1189  }
1190  else {
1191  OTString strNymID(OTHER_NYM_ID);
1192  otErr << "Failure loading or verifying Other Nym public key in "
1193  "OTMarket::" << __FUNCTION__ << ": " << strNymID << "\n";
1194  pOtherTrade->FlagForRemoval();
1195  return;
1196  }
1197  }
1198 
1199  // AT THIS POINT, I have pServerNym, pFirstNym, and pOtherNym.
1200  // ALL are loaded from disk (where necessary.) AND...
1201  // ALL are valid pointers, (even if they sometimes point to the same
1202  // object,)
1203  // AND none are capable of overwriting the storage of the other (by
1204  // accidentally
1205  // loading the same storage twice.)
1206  // We also have boolean variables at this point to tell us exactly which are
1207  // which,
1208  // (in case some of those pointers do go to the same object.)
1209  // They are:
1210  // bFirstNymIsServerNym, bOtherNymIsServerNym, and bTradersAreSameNym.
1211  //
1212  // We also have theTrade, theOffer, pOtherTrade, and theOtherOffer,
1213  // and we know they are all good also.
1214  //
1215  // We also know that pOtherTrade/theOtherOffer are a Limit order (NOT a
1216  // Market order.)
1217  // (Meaning pOtherTrade/theOtherOffer definitely has a price set.)
1218  //
1219  // We also know that theTrade/theOffer MIGHT be a Limit order OR a Market
1220  // order.
1221  // (Meaning theTrade/theOffer MIGHT have a 0 price.)
1222  //
1223 
1224  // Make sure have ALL FOUR accounts loaded and checked out.
1225  // (first nym's asset/currency, and other nym's asset/currency.)
1226 
1227  OTAccount* pFirstAssetAcct =
1228  OTAccount::LoadExistingAccount(theTrade.GetSenderAcctID(), SERVER_ID);
1229  OTAccount* pFirstCurrencyAcct =
1230  OTAccount::LoadExistingAccount(theTrade.GetCurrencyAcctID(), SERVER_ID);
1231 
1232  OTAccount* pOtherAssetAcct = OTAccount::LoadExistingAccount(
1233  pOtherTrade->GetSenderAcctID(), SERVER_ID);
1234  OTAccount* pOtherCurrencyAcct = OTAccount::LoadExistingAccount(
1235  pOtherTrade->GetCurrencyAcctID(), SERVER_ID);
1236 
1237  if ((nullptr == pFirstAssetAcct) || (nullptr == pFirstCurrencyAcct)) {
1238  otOut << "ERROR verifying existence of one of the first trader's "
1239  "accounts during attempted Market trade.\n";
1240  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct,
1241  pOtherAssetAcct, pOtherCurrencyAcct);
1242  theTrade.FlagForRemoval(); // Removes from Cron.
1243  return;
1244  }
1245  else if ((nullptr == pOtherAssetAcct) ||
1246  (nullptr == pOtherCurrencyAcct)) {
1247  otOut << "ERROR verifying existence of one of the second trader's "
1248  "accounts during attempted Market trade.\n";
1249  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct,
1250  pOtherAssetAcct, pOtherCurrencyAcct);
1251  pOtherTrade->FlagForRemoval(); // Removes from Cron.
1252  return;
1253  }
1254 
1255  // Are the accounts of the first trader of the right asset types?
1256  // We already know the asset types matched as far as the market, trades, and
1257  // offers were concerned.
1258  // But only once the accounts themselves have been loaded can we VERIFY this
1259  // to be true.
1260  else if ((pFirstAssetAcct->GetAssetTypeID() !=
1261  GetAssetID()) || // the trader's asset accts have same asset type
1262  // as the market.
1263  (pFirstCurrencyAcct->GetAssetTypeID() !=
1264  GetCurrencyID()) // the trader's currency accts have same asset
1265  // type as the market.
1266  ) {
1267  otErr << "ERROR - First Trader has accounts of wrong "
1268  "asset types in OTMarket::" << __FUNCTION__ << "\n";
1269  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct,
1270  pOtherAssetAcct, pOtherCurrencyAcct);
1271  theTrade.FlagForRemoval(); // Removes from Cron.
1272  return;
1273  }
1274  else if ((pOtherAssetAcct->GetAssetTypeID() !=
1275  GetAssetID()) || // the trader's asset accts have same asset
1276  // type as the market.
1277  (pOtherCurrencyAcct->GetAssetTypeID() !=
1278  GetCurrencyID())) // the trader's currency accts have same asset
1279  // type as market.
1280  {
1281  otErr << "ERROR - Other Trader has accounts of wrong "
1282  "asset types in OTMarket::" << __FUNCTION__ << "\n";
1283  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct,
1284  pOtherAssetAcct, pOtherCurrencyAcct);
1285  pOtherTrade->FlagForRemoval(); // Removes from Cron.
1286  return;
1287  }
1288 
1289  // Make sure all accounts are signed by the server and have the owner they
1290  // are expected to have.
1291 
1292  // I call VerifySignature here since VerifyContractID was already called in
1293  // LoadExistingAccount().
1294  else if ((!pFirstAssetAcct->VerifyOwner(*pFirstNym) ||
1295  !pFirstAssetAcct->VerifySignature(*pServerNym)) ||
1296  (!pFirstCurrencyAcct->VerifyOwner(*pFirstNym) ||
1297  !pFirstCurrencyAcct->VerifySignature(*pServerNym))) {
1298  otErr << "ERROR verifying ownership or signature on one of first "
1299  "trader's accounts in OTMarket::" << __FUNCTION__ << "\n";
1300  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct,
1301  pOtherAssetAcct, pOtherCurrencyAcct);
1302  theTrade.FlagForRemoval(); // Removes from Cron.
1303  return;
1304  }
1305  else if ((!pOtherAssetAcct->VerifyOwner(*pOtherNym) ||
1306  !pOtherAssetAcct->VerifySignature(*pServerNym)) ||
1307  (!pOtherCurrencyAcct->VerifyOwner(*pOtherNym) ||
1308  !pOtherCurrencyAcct->VerifySignature(*pServerNym))) {
1309  otErr << "ERROR verifying ownership or signature on one of other "
1310  "trader's accounts in OTMarket::" << __FUNCTION__ << "\n";
1311  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct,
1312  pOtherAssetAcct, pOtherCurrencyAcct);
1313  pOtherTrade->FlagForRemoval(); // Removes from Cron.
1314  return;
1315  }
1316 
1317  // By this point, I know I have all four accounts loaded, and I know that
1318  // they have the right asset types,
1319  // and I know they have the right owners and they were all signed by the
1320  // server.
1321  // I also know that their account IDs in their internal records matched the
1322  // account filename for each acct.
1323  // I also have pointers to the Nyms who own these accounts, as well as the
1324  // Trades and Offers associated with them.
1325  else {
1326  // Okay then, everything checks out. Let's add this to the sender's
1327  // outbox and the recipient's inbox.
1328  // IF they can be loaded up from file, or generated, that is.
1329 
1330  // Load the inbox/outbox in case they already exist
1331  OTLedger theFirstAssetInbox(FIRST_NYM_ID, theTrade.GetSenderAcctID(),
1332  SERVER_ID),
1333  theFirstCurrencyInbox(FIRST_NYM_ID, theTrade.GetCurrencyAcctID(),
1334  SERVER_ID),
1335  theOtherAssetInbox(OTHER_NYM_ID, pOtherTrade->GetSenderAcctID(),
1336  SERVER_ID),
1337  theOtherCurrencyInbox(OTHER_NYM_ID,
1338  pOtherTrade->GetCurrencyAcctID(), SERVER_ID);
1339 
1340  // ALL inboxes -- no outboxes. All will receive notification of
1341  // something ALREADY DONE.
1342  bool bSuccessLoadingFirstAsset = theFirstAssetInbox.LoadInbox();
1343  bool bSuccessLoadingFirstCurrency = theFirstCurrencyInbox.LoadInbox();
1344  bool bSuccessLoadingOtherAsset = theOtherAssetInbox.LoadInbox();
1345  bool bSuccessLoadingOtherCurrency = theOtherCurrencyInbox.LoadInbox();
1346 
1347  // ...or generate them otherwise...
1348 
1349  if (true == bSuccessLoadingFirstAsset)
1350  bSuccessLoadingFirstAsset =
1351  theFirstAssetInbox.VerifyAccount(*pServerNym);
1352  else
1353  bSuccessLoadingFirstAsset = theFirstAssetInbox.GenerateLedger(
1354  theTrade.GetSenderAcctID(), SERVER_ID, OTLedger::inbox,
1355  true); // bGenerateFile=true
1356 
1357  if (true == bSuccessLoadingFirstCurrency)
1358  bSuccessLoadingFirstCurrency =
1359  theFirstCurrencyInbox.VerifyAccount(*pServerNym);
1360  else
1361  bSuccessLoadingFirstCurrency = theFirstCurrencyInbox.GenerateLedger(
1362  theTrade.GetCurrencyAcctID(), SERVER_ID, OTLedger::inbox,
1363  true); // bGenerateFile=true
1364 
1365  if (true == bSuccessLoadingOtherAsset)
1366  bSuccessLoadingOtherAsset =
1367  theOtherAssetInbox.VerifyAccount(*pServerNym);
1368  else
1369  bSuccessLoadingOtherAsset = theOtherAssetInbox.GenerateLedger(
1370  pOtherTrade->GetSenderAcctID(), SERVER_ID, OTLedger::inbox,
1371  true); // bGenerateFile=true
1372 
1373  if (true == bSuccessLoadingOtherCurrency)
1374  bSuccessLoadingOtherCurrency =
1375  theOtherCurrencyInbox.VerifyAccount(*pServerNym);
1376  else
1377  bSuccessLoadingOtherCurrency = theOtherCurrencyInbox.GenerateLedger(
1378  pOtherTrade->GetCurrencyAcctID(), SERVER_ID, OTLedger::inbox,
1379  true); // bGenerateFile=true
1380 
1381  if ((false == bSuccessLoadingFirstAsset) ||
1382  (false == bSuccessLoadingFirstCurrency)) {
1383  otErr << "ERROR loading or generating an inbox for first trader in "
1384  "OTMarket::" << __FUNCTION__ << ".\n";
1385  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct,
1386  pOtherAssetAcct, pOtherCurrencyAcct);
1387  theTrade.FlagForRemoval(); // Removes from Cron.
1388  return;
1389  }
1390  else if ((false == bSuccessLoadingOtherAsset) ||
1391  (false == bSuccessLoadingOtherCurrency)) {
1392  otErr << "ERROR loading or generating an inbox for other trader in "
1393  "OTMarket::" << __FUNCTION__ << ".\n";
1394  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct,
1395  pOtherAssetAcct, pOtherCurrencyAcct);
1396  pOtherTrade->FlagForRemoval(); // Removes from Cron.
1397  return;
1398  }
1399  else {
1400  // Generate new transaction numbers for these new transactions
1401  int64_t lNewTransactionNumber = pCron->GetNextTransactionNumber();
1402 
1403  // OT_ASSERT(lNewTransactionNumber > 0); // this can be
1404  // my reminder.
1405  if (0 == lNewTransactionNumber) {
1406  otOut << "WARNING: Market is unable to process because there "
1407  "are no more transaction numbers available.\n";
1408  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct,
1409  pOtherAssetAcct, pOtherCurrencyAcct);
1410  // (Here I flag neither trade for removal.)
1411  return;
1412  }
1413 
1414  // Why a new transaction number here?
1415  //
1416  // Each user had to burn one of his own numbers to generate his own
1417  // Trade.
1418  //
1419  // So the First Trader might have used number 345, and the Other
1420  // Trader might have
1421  // used the number 912. Those numbers were used to CREATE the Trades
1422  // initially, and
1423  // they can be used to lookup the original trade request from each
1424  // user.
1425  //
1426  // So whenever I give a receipt to either Trader, I am sure to tell
1427  // Trader A that
1428  // this is in reference to transaction 345, and I tell Trader B that
1429  // this is in
1430  // reference to transaction 912.
1431  //
1432  // I also have to get a new transaction number (here) because the
1433  // first two numbers
1434  // were requests signed by the users to post the trades. That was
1435  // completed--those
1436  // numbers have been used now, and they could not be used again
1437  // without introducing
1438  // duplication and confusion. To remove money from someone's
1439  // account, which we have just
1440  // done, you need a new transaction number for that! Because you
1441  // need a new number if
1442  // you're going to put some new receipt in their inbox, which is
1443  // exactly what we are
1444  // doing right now.
1445  //
1446  // So let's say I generate transaction # 10023 to represent this
1447  // money exchange that has
1448  // just occurred between these two traders. To me as a user, 345
1449  // was my original request
1450  // to post the trade, 912 was the other guy's request to post his
1451  // trade, and 10023 was
1452  // the server's transferring funds (based on the authorization in
1453  // those trades). Put
1454  // another way, 345 has my signature, 912 has the other trader's
1455  // signature, and 10023
1456  // has the server's signature.
1457 
1458  // Start generating the receipts (for all four inboxes.)
1459 
1460  OTTransaction* pTrans1 = OTTransaction::GenerateTransaction(
1461  theFirstAssetInbox, OTTransaction::marketReceipt,
1462  lNewTransactionNumber);
1463 
1464  OTTransaction* pTrans2 = OTTransaction::GenerateTransaction(
1465  theFirstCurrencyInbox, OTTransaction::marketReceipt,
1466  lNewTransactionNumber);
1467 
1468  OTTransaction* pTrans3 = OTTransaction::GenerateTransaction(
1469  theOtherAssetInbox, OTTransaction::marketReceipt,
1470  lNewTransactionNumber);
1471 
1472  OTTransaction* pTrans4 = OTTransaction::GenerateTransaction(
1473  theOtherCurrencyInbox, OTTransaction::marketReceipt,
1474  lNewTransactionNumber);
1475 
1476  // (No need to OT_ASSERT on the above transactions since it occurs
1477  // in GenerateTransaction().)
1478 
1479  // All four inboxes will get receipts with the same (new)
1480  // transaction ID.
1481  // They all have a "reference to" containing the original trade.
1482  // The first two will contain theTrade as the reference field,
1483  // but the second two contain pOtherTrade as the reference field.
1484  // The first two also thus reference a different original
1485  // transaction number than the other two.
1486  // That's because each end of the trade was originally authorized
1487  // separately by each trader, and
1488  // in each case he used his own unique transaction number to do so.
1489 
1490  // set up the transaction items (each transaction may have multiple
1491  // items... but not in this case.)
1492  OTItem* pItem1 = OTItem::CreateItemFromTransaction(
1493  *pTrans1, OTItem::marketReceipt);
1494  OTItem* pItem2 = OTItem::CreateItemFromTransaction(
1495  *pTrans2, OTItem::marketReceipt);
1496  OTItem* pItem3 = OTItem::CreateItemFromTransaction(
1497  *pTrans3, OTItem::marketReceipt);
1498  OTItem* pItem4 = OTItem::CreateItemFromTransaction(
1499  *pTrans4, OTItem::marketReceipt);
1500 
1501  // these may be unnecessary, I'll have to check
1502  // CreateItemFromTransaction. I'll leave em.
1503  OT_ASSERT(nullptr != pItem1);
1504  OT_ASSERT(nullptr != pItem2);
1505  OT_ASSERT(nullptr != pItem3);
1506  OT_ASSERT(nullptr != pItem4);
1507 
1508  pItem1->SetStatus(OTItem::rejection); // the default.
1509  pItem2->SetStatus(OTItem::rejection); // the default.
1510  pItem3->SetStatus(OTItem::rejection); // the default.
1511  pItem4->SetStatus(OTItem::rejection); // the default.
1512 
1513  // Calculate the amount and remove / add it to the relevant
1514  // accounts.
1515  // Make sure each Account can afford it, and roll back in case of
1516  // failure.
1517 
1518  // -- TheTrade will get the PriceLimit offered by theOtherOffer, and
1519  // not vice-versa.
1520  // (See explanation below this function if you need to know the
1521  // reasoning.)
1522 
1523  // NOTICE: This is one of the reasons why theOtherOffer CANNOT be a
1524  // Market order,
1525  // because we will be using his price limit to determine the price.
1526  // (Another reason FYI, is because Market Orders are only allowed to
1527  // process once,
1528  // IN TURN.)
1529 
1530  // Some logic described:
1531  //
1532  // Calculate the price
1533  // I know the amount available for trade is at LEAST the minimum
1534  // increment on both
1535  // sides, since that was validated before this function was even
1536  // called. Therefore,
1537  // let's calculate a price based on the largest of the two minimum
1538  // increments.
1539  // Figure out which is largest, then divide that by the scale to get
1540  // the multiplier.
1541  // Multiply THAT by the theOtherOffer's Price Limit to get the price
1542  // per minimum increment.
1543  //
1544  // Since we are calculating the MINIMUM that must be processed per
1545  // round, let's also
1546  // calculate the MOST that could be traded between these two offers.
1547  // Then let's
1548  // mod that to the Scale and remove the remainder, then divide by
1549  // the Scale and
1550  // multiply by the price (to get the MOST that would have to be
1551  // paid, if the offers
1552  // fulfilled each other to the maximum possible according to their
1553  // limits.)
1554  // !! It's better to process it all at once and avoid the loop
1555  // entirely.
1556  // Plus, there's SUPPOSED to be enough funds in all the accounts to
1557  // cover it.
1558  //
1559  // Anyway, if there aren't:
1560  //
1561  // Then LOOP (while available >= minimum increment) on both sides of
1562  // the trade
1563  // Inside, try to move funds across all 4 accounts. If any of them
1564  // fail, roll them
1565  // back and break. (I'll check balances first to avoid this.)
1566  // As long as there's enough available in the accounts to continue
1567  // exchanging the
1568  // largest minimum increment, then keep looping like this and moving
1569  // the digital assets.
1570  // Each time, also, be sure to update the Offer so that less and
1571  // less is available on each trade.
1572  // At some point, the Trades will run out of authorization to
1573  // transfer any more from the accounts.
1574  // Perhaps one has a 10,000 bushel total limit and it has been
1575  // reached. The trade is complete,
1576  // from his side of it, anyway. Then the loop will be over for sure.
1577 
1578  OTAccount* pAssetAccountToDebit = nullptr;
1579  OTAccount* pAssetAccountToCredit = nullptr;
1580  OTAccount* pCurrencyAccountToDebit = nullptr;
1581  OTAccount* pCurrencyAccountToCredit = nullptr;
1582 
1583  if (theOffer.IsAsk()) // I'm selling, he's buying
1584  {
1585  pAssetAccountToDebit = pFirstAssetAcct; // I am selling gold.
1586  // When I sell, my gold
1587  // balance goes down.
1588  pAssetAccountToCredit =
1589  pOtherAssetAcct; // He is bidding on gold. When he buys, his
1590  // gold balance goes up.
1591  pCurrencyAccountToDebit =
1592  pOtherCurrencyAcct; // He is paying in dollars. When he
1593  // pays, his dollar balance goes down.
1594  pCurrencyAccountToCredit =
1595  pFirstCurrencyAcct; // I am being paid in dollars. When I
1596  // get paid, my dollar balance goes up.
1597  }
1598  else // I'm buying, he's selling
1599  {
1600  pAssetAccountToDebit =
1601  pOtherAssetAcct; // He is selling gold. When he sells, his
1602  // gold balance goes down.
1603  pAssetAccountToCredit =
1604  pFirstAssetAcct; // I am bidding on gold. When I buy, my
1605  // gold balance goes up.
1606  pCurrencyAccountToDebit =
1607  pFirstCurrencyAcct; // I am paying in dollars. When I pay,
1608  // my dollar balance goes down.
1609  pCurrencyAccountToCredit =
1610  pOtherCurrencyAcct; // He is being paid in dollars. When he
1611  // gets paid, his dollar balance goes
1612  // up.
1613  }
1614 
1615  // Calculate minimum increment to be traded each round.
1616  int64_t lMinIncrementPerRound =
1617  ((theOffer.GetMinimumIncrement() >
1618  theOtherOffer.GetMinimumIncrement())
1619  ? theOffer.GetMinimumIncrement()
1620  : theOtherOffer.GetMinimumIncrement());
1621 
1622  const int64_t lMultiplier =
1623  (lMinIncrementPerRound / GetScale()); // If the Market scale is
1624  // 10, and the minimum
1625  // increment is 50,
1626  // multiplier is 5..
1627  // The price limit is per scale. (Per 10.) So if 1oz gold is $1300,
1628  // then 10oz scale
1629  // would be $13,000. So if my price limit is per SCALE, I might set
1630  // my limit
1631  // to $12,000 or $13,000 (PER 10 OZ OF GOLD, which is the SCALE for
1632  // this market.)
1633 
1634  // Calc price of each round.
1635  int64_t lPrice =
1636  (lMultiplier * theOtherOffer.GetPriceLimit()); // So if my
1637  // minimum
1638  // increment is
1639  // 50, then my
1640  // multiplier is
1641  // 5, which means
1642  // multiply my price by 5: $13,000 * 5 == $65,000 for 50 oz. per
1643  // minimum inc.
1644 
1645  // Why am I using the OTHER Offer's price limit, and not my own?
1646  // See notes at top and bottom of this function for the answer.
1647 
1648  // There's two ways to process this: in rounds (by minimum
1649  // increment), for which the numbers were
1650  // just calulated above...
1651  //
1652  // ...OR based on whatever is the MOST available from BOTH parties.
1653  // (Whichever is the least of the
1654  // two parties' "Most Available Left To Trade".) So I'll try THAT
1655  // first, to avoid processing in
1656  // rounds. (Since the funds SHOULD be there...)
1657 
1658  int64_t lMostAvailable = ((theOffer.GetAmountAvailable() >
1659  theOtherOffer.GetAmountAvailable())
1660  ? theOtherOffer.GetAmountAvailable()
1661  : theOffer.GetAmountAvailable());
1662 
1663  int64_t lTemp = lMostAvailable % GetScale(); // The Scale may not
1664  // evenly divide into
1665  // the amount available
1666 
1667  lMostAvailable -= lTemp; // We'll subtract remainder amount, so it's
1668  // even to scale (which is how it's
1669  // priced.)
1670 
1671  // We KNOW the amount available on the offer is at least as much as
1672  // the minimum increment (on both sides)
1673  // because that is verified in the caller function. So we KNOW
1674  // either side can process the minimum, at least
1675  // based on the authorization in the offers (not necessarily in the
1676  // accounts themselves, though they are
1677  // SUPPOSED to have enough funds to cover it...)
1678  //
1679  // Next question is: can both sides process the MOST AVAILABLE? If
1680  // so, do THAT, instead of processing by rounds.
1681 
1682  const int64_t lOverallMultiplier =
1683  lMostAvailable / GetScale(); // Price is per scale // This line
1684  // was commented with the line
1685  // below it. They go together.
1686 
1687  // Why theOtherOffer's price limit instead of theOffer's? See notes
1688  // top/bottom this function.
1689  const int64_t lMostPrice =
1690  (lOverallMultiplier * theOtherOffer.GetPriceLimit());
1691  // TO REMOVE MULTIPLIER FROM PRICE, AT LEAST THE ABOVE LINE WOULD
1692  // REMOVE MULTIPLIER.
1693 
1694  // To avoid rounds, first I see if I can satisfy the entire order at
1695  // once on either side...
1696  if ((pAssetAccountToDebit->GetBalance() >= lMostAvailable) &&
1697  (pCurrencyAccountToDebit->GetBalance() >=
1698  lMostPrice)) { // There's enough the accounts to do it all at
1699  // once! No need for rounds.
1700 
1701  lMinIncrementPerRound = lMostAvailable;
1702  lPrice = lMostPrice;
1703 
1704  // By setting the ABOVE two values the way that I did, it means
1705  // the below loop
1706  // will execute properly, BUT ONLY ITERATING ONCE. Basically
1707  // this section of
1708  // code just optimizes that loop when possible by allowing it to
1709  // execute
1710  // only once.
1711  }
1712 
1713  // Otherwise, I go ahead and process it in rounds, by minimum
1714  // increment...
1715  // (Since the funds ARE supposed to be there to process the whole
1716  // thing, I COULD
1717  // choose to cancel the offender right now! I might put an extra fee
1718  // in this loop or
1719  // just remove it. The software would still be functional, it would
1720  // just enforce the
1721  // account having enough funds to cover the full offer at all
1722  // times--if it wants to
1723  // trade at all.)
1724 
1725  bool bSuccess = false;
1726 
1727  int64_t lOfferFinished = 0,
1728  lOtherOfferFinished = 0, // We store these up and then add
1729  // the totals to the offers at the
1730  // end (only upon success.)
1731  lTotalPaidOut =
1732  0; // However much is paid for the assets, total.
1733 
1734  // Continuing the example from above, each round I will trade:
1735  // 50 oz lMinIncrementPerRound, in return for $65,000 lPrice.
1736  while ((lMinIncrementPerRound <=
1737  (theOffer.GetAmountAvailable() -
1738  lOfferFinished)) && // The primary offer has at least 50
1739  // available to trade (buy OR sell)
1740  (lMinIncrementPerRound <=
1741  (theOtherOffer.GetAmountAvailable() -
1742  lOtherOfferFinished)) && // The other offer has at least 50
1743  // available for trade also.
1744  (lMinIncrementPerRound <=
1745  pAssetAccountToDebit->GetBalance()) && // Asset Acct to be
1746  // debited has at
1747  // least 50
1748  // available.
1749  (lPrice <=
1750  pCurrencyAccountToDebit->GetBalance())) // Currency Acct to
1751  // be debited has at
1752  // least 65000
1753  // available.
1754  {
1755  // Within this block, the offer is authorized on both sides, and
1756  // there is enough in
1757  // each of the relevant accounts to cover the round, (for SURE.)
1758  // So let's DO it.
1759 
1760  bool bMove1 =
1761  pAssetAccountToDebit->Debit(lMinIncrementPerRound);
1762  bool bMove2 = pCurrencyAccountToDebit->Debit(lPrice);
1763  bool bMove3 =
1764  pAssetAccountToCredit->Credit(lMinIncrementPerRound);
1765  bool bMove4 = pCurrencyAccountToCredit->Credit(lPrice);
1766 
1767  // If ANY of these failed, then roll them all back and break.
1768  if (!bMove1 || !bMove2 || !bMove3 || !bMove4) {
1769  otErr << "Very strange! Funds were available, yet debit or "
1770  "credit failed while performing trade. "
1771  "Attempting rollback!\n";
1772  // We won't save the files anyway, if this failed. So the
1773  // rollback is actually superfluous but somehow worthwhile.
1775  *pAssetAccountToDebit, bMove1, lMinIncrementPerRound,
1776  *pCurrencyAccountToDebit, bMove2, lPrice,
1777  *pAssetAccountToCredit, bMove3, lMinIncrementPerRound,
1778  *pCurrencyAccountToCredit, bMove4, lPrice);
1779 
1780  bSuccess = false;
1781  break;
1782  }
1783 
1784  // At this point, we know all the debits and credits were
1785  // successful (FOR THIS ROUND.)
1786  // Also notice that the Trades and Offers have not been changed
1787  // at all--only the accounts.
1788  bSuccess = true;
1789 
1790  // So let's adjust the offers to reflect this also...
1791  // The while() above checks these values in
1792  // GetAmountAvailable().
1793  lOfferFinished += lMinIncrementPerRound;
1794  lOtherOfferFinished += lMinIncrementPerRound;
1795 
1796  lTotalPaidOut += lPrice;
1797  }
1798 
1799  // At this point, it has gone god-knows-how-many rounds, (or just
1800  // one) and then broke out,
1801  // with bSuccess=false, OR finished properly when the while() terms
1802  // were fulfilled, with bSuccess = true.
1803  //
1804  // My point? If there was a screw-up, EVEN if the rollback was
1805  // successful, it STILL only rolled-back
1806  // the most RECENT round -- There still might have been 20 rounds
1807  // processed before the break.
1808  // (Funds could still have been moved, even if rollback was
1809  // successful.) THEREFORE, DO NOT SAVE if false.
1810  // We only save those accounts if bSuccess == true. The Rollback is
1811  // not good enough
1812 
1813  if (true == bSuccess) {
1814  // ALL of the four accounts involved need to get a receipt of
1815  // this trade in their inboxes...
1816  pItem1->SetStatus(OTItem::acknowledgement); // pFirstAssetAcct
1817  pItem2->SetStatus(
1818  OTItem::acknowledgement); // pFirstCurrencyAcct
1819  pItem3->SetStatus(OTItem::acknowledgement); // pOtherAssetAcct
1820  pItem4->SetStatus(
1821  OTItem::acknowledgement); // pOtherCurrencyAcct
1822 
1823  // Everytime a trade processes, a receipt is put in each
1824  // account's inbox.
1825  // This contains a copy of the current trade (which took money
1826  // from the acct.)
1827  //
1828  // ==> So I increment the trade count each time before dropping
1829  // the receipt. (I also use a fresh
1830  // transaction number when I put it into the inbox.) That way,
1831  // the user will never get the same
1832  // receipt for the same trade twice. It cannot take funds from
1833  // his account, without a new trade
1834  // count and a new transaction number on a new receipt. Until
1835  // the user accepts the receipt out
1836  // of his inbox with a new balance agreement, the existing
1837  // receipts can be added up and compared
1838  // to the last balance agreement, to verify the current balance.
1839  // Every receipt from a processing
1840  // trade will have the user's authorization, signature, and
1841  // terms, as well as the update in balances
1842  // due to the trade, signed by the server.
1843 
1844  theTrade.IncrementTradesAlreadyDone();
1845  pOtherTrade->IncrementTradesAlreadyDone();
1846 
1847  theOffer.IncrementFinishedSoFar(lOfferFinished); // I was
1848  // storing
1849  // these up in
1850  // the loop
1851  // above.
1852  theOtherOffer.IncrementFinishedSoFar(
1853  lOtherOfferFinished); // I was storing these up in the loop
1854  // above.
1855 
1856  // These have updated values, so let's save them.
1857  theTrade.ReleaseSignatures();
1858  theTrade.SignContract(*pServerNym);
1859  theTrade.SaveContract();
1860 
1861  pOtherTrade->ReleaseSignatures();
1862  pOtherTrade->SignContract(*pServerNym);
1863  pOtherTrade->SaveContract();
1864 
1865  theOffer.ReleaseSignatures();
1866  theOffer.SignContract(*pServerNym);
1867  theOffer.SaveContract();
1868 
1869  theOtherOffer.ReleaseSignatures();
1870  theOtherOffer.SignContract(*pServerNym);
1871  theOtherOffer.SaveContract();
1872 
1873  m_lLastSalePrice =
1874  theOtherOffer.GetPriceLimit(); // Priced per scale.
1875 
1876  // Here we save this trade in a list of the most recent 50
1877  // trades.
1878  {
1879  if (nullptr == m_pTradeList) {
1880  m_pTradeList = dynamic_cast<OTDB::TradeListMarket*>(
1883  }
1884 
1885  std::unique_ptr<OTDB::TradeDataMarket> pTradeData(
1886  dynamic_cast<OTDB::TradeDataMarket*>(OTDB::CreateObject(
1888 
1889  const int64_t& lTransactionNum =
1890  theOffer.GetTransactionNum();
1891  const time64_t theDate = OTTimeGetCurrentTime();
1892  const int64_t& lPriceLimit =
1893  theOtherOffer.GetPriceLimit(); // Priced per scale.
1894  const int64_t& lAmountSold = lOfferFinished;
1895 
1896  pTradeData->transaction_id =
1897  to_string<int64_t>(lTransactionNum);
1898  pTradeData->date = to_string<time64_t>(theDate);
1899  pTradeData->price = to_string<int64_t>(lPriceLimit);
1900  pTradeData->amount_sold = to_string<int64_t>(lAmountSold);
1901 
1902  m_strLastSaleDate = pTradeData->date;
1903 
1904  // *pTradeData is CLONED at this time (I'm still responsible
1905  // to delete.)
1906  // That's also why I add it here, after all the above: So
1907  // the data is set right BEFORE the cloning occurs.
1908  //
1909  m_pTradeList->AddTradeDataMarket(*pTradeData);
1910 
1911  // Here we erase the oldest elements so the list never
1912  // exceeds 50 elements total.
1913  //
1914  while (m_pTradeList->GetTradeDataMarketCount() >
1916  m_pTradeList->RemoveTradeDataMarket(0);
1917  }
1918 
1919  // Account balances have changed based on these trades that we
1920  // just processed.
1921  // Make sure to save the Market since it contains those offers
1922  // that have just updated.
1923  SaveMarket();
1924 
1925  // The Trade has changed, and it is stored as a CronItem. So I
1926  // save Cron as well, for
1927  // the same reason I saved the Market.
1928  pCron->SaveCron();
1929  }
1930 
1931  //
1932  // EVERYTHING BELOW is just about notifying the users, by dropping
1933  // the receipt in their
1934  // inboxes.
1935  //
1936  // (The Trade, Offer, Cron, and Market are ALL updated and SAVED as
1937  // of this point.)
1938  //
1939 
1940  // The TRANSACTION will be sent with "In Reference To" information
1941  // containing the
1942  // ORIGINAL SIGNED TRADE (which includes the ORIGINAL SIGNED OFFER
1943  // inside of it.)
1944  //
1945  // Whereas the TRANSACTION ITEM includes a "Note" containing the
1946  // UPDATED TRADE,
1947  // (with the SERVER's SIGNATURE) as well as an "attachment"
1948  // containing the UPDATED
1949  // OFFER (again, with the server's signature on it.)
1950 
1951  // I was doing this above, but had to move it all down here, since
1952  // the Trade
1953  // and Offer have just finally been updated to their final
1954  // values...
1955 
1956  // Here I make sure that each trader's receipt (each inbox notice)
1957  // references the
1958  // transaction number that the trader originally used to issue the
1959  // trade...
1960  // This number is used to match up offers to trades, and used to
1961  // track all cron items.
1962  // (All Cron items require a transaction from the user to add them
1963  // to Cron in the
1964  // first place.)
1965  //
1966  pTrans1->SetReferenceToNum(theTrade.GetTransactionNum());
1967  pTrans2->SetReferenceToNum(theTrade.GetTransactionNum());
1968  pTrans3->SetReferenceToNum(pOtherTrade->GetTransactionNum());
1969  pTrans4->SetReferenceToNum(pOtherTrade->GetTransactionNum());
1970 
1971  // Then, I make sure the Reference string on the Transaction
1972  // contains the
1973  // ORIGINAL TRADE (with the TRADER's SIGNATURE ON IT!) For both
1974  // traders.
1975 
1976  OTCronItem* pOrigTrade = nullptr;
1977  OTCronItem* pOrigOtherTrade = nullptr;
1978 
1979  // OTCronItem::LoadCronReceipt loads the original version with the
1980  // user's signature.
1981  // (Updated versions, as processing occurs, are signed by the
1982  // server.)
1983  pOrigTrade =
1984  OTCronItem::LoadCronReceipt(theTrade.GetTransactionNum());
1985  pOrigOtherTrade =
1986  OTCronItem::LoadCronReceipt(pOtherTrade->GetTransactionNum());
1987 
1988  OT_ASSERT(nullptr != pOrigTrade);
1989  OT_ASSERT(nullptr != pOrigOtherTrade);
1990 
1991  OT_ASSERT_MSG(pOrigTrade->VerifySignature(*pFirstNym),
1992  "Signature was already verified on Trade when first "
1993  "added to market, but now it fails.\n");
1994  OT_ASSERT_MSG(pOrigOtherTrade->VerifySignature(*pOtherNym),
1995  "Signature was already verified on Trade when first "
1996  "added to market, but now it fails.\n");
1997 
1998  // I now have String copies (PGP-signed XML files) of the original
1999  // Trade requests...
2000  // Plus I know they were definitely signed by the Nyms (even though
2001  // that was already
2002  // verified when they were first added to the market--and they have
2003  // been signed by the
2004  // server nym ever since.)
2005  OTString strOrigTrade(*pOrigTrade),
2006  strOrigOtherTrade(*pOrigOtherTrade);
2007 
2008  // The reference on the transaction contains OTCronItem, in this
2009  // case.
2010  // The original trade for each party, versus the updated trade
2011  // (which is stored
2012  // on the marketReceipt item just below here.)
2013  //
2014  pTrans1->SetReferenceString(strOrigTrade);
2015  pTrans2->SetReferenceString(strOrigTrade);
2016  pTrans3->SetReferenceString(strOrigOtherTrade);
2017  pTrans4->SetReferenceString(strOrigOtherTrade);
2018 
2019  // Make sure to clean these up.
2020  // TODO: Run a scanner on the code for memory leaks and buffer
2021  // overflows.
2022  delete pOrigTrade;
2023  pOrigTrade = nullptr;
2024  delete pOrigOtherTrade;
2025  pOrigOtherTrade = nullptr;
2026 
2027  // Here's where the item stores the UPDATED TRADE (in its Note)
2028  // and the UPDATED OFFER (in its attachment) with the SERVER's
2029  // SIGNATURE
2030  // on both.
2031  // (As a receipt for each trader, so they can see their offer
2032  // updating.)
2033 
2034  // Lucky I just signed and saved these trades / offers above, or
2035  // they would
2036  // still have the old data in them.
2037  OTString strTrade(theTrade), strOtherTrade(*pOtherTrade),
2038  strOffer(theOffer), strOtherOffer(theOtherOffer);
2039 
2040  // The marketReceipt ITEM's NOTE contains the UPDATED TRADE.
2041  //
2042  pItem1->SetNote(strTrade);
2043  pItem2->SetNote(strTrade);
2044  pItem3->SetNote(strOtherTrade);
2045  pItem4->SetNote(strOtherTrade);
2046 
2047  /*
2048 
2049  NOTE, todo: Someday, need to reverse these, so that the updated
2050  Trade is stored in
2051  the attachment, and the updated offer is stored in the note. This
2052  is much more consistent
2053  with other cron receipts, such as paymentReceipt, and finalReceipt.
2054  Unfortunately,
2055  marketReceipt is already implemented the opposite of these, but I
2056  will fix it someday just
2057  for consistency. See large notes 2/3rds of the way down in
2058  OTTrade::onFinalReceipt().
2059 
2060  Todo security: really each receipt should contain a copy of BOTH
2061  (asset+currency) so
2062  the user can calculate the sale price and compare it to the terms
2063  on the original offer.
2064  */
2065 
2066  // Also set the ** UPDATED OFFER ** as the ATTACHMENT on the **
2067  // item.**
2068  // (With the SERVER's signature on it!)
2069  // (As a receipt for each trader, so they can see their offer
2070  // updating.)
2071  pItem1->SetAttachment(strOffer);
2072  pItem2->SetAttachment(strOffer);
2073  pItem3->SetAttachment(strOtherOffer);
2074  pItem4->SetAttachment(strOtherOffer);
2075 
2076  // Inbox receipts need to clearly show the AMOUNT moved...
2077  // Also need to clearly show negative or positive, since that
2078  // is otherwise not obvious just because you have a marketReceipt...
2079  //
2080  // The AMOUNT is stored on the marketReceipt ITEM, on the item list
2081  // for
2082  // the marketReceipt TRANSACTION.
2083  //
2084  if (theOffer.IsAsk()) // I'm selling, he's buying
2085  {
2086  pItem1->SetAmount(lOfferFinished * (-1)); // first asset
2087  pItem2->SetAmount(lTotalPaidOut); // first currency
2088  pItem3->SetAmount(lOtherOfferFinished); // other asset
2089  pItem4->SetAmount(lTotalPaidOut * (-1)); // other currency
2090  }
2091  else // I'm buying, he's selling
2092  {
2093  pItem1->SetAmount(lOfferFinished); // first asset
2094  pItem2->SetAmount(lTotalPaidOut * (-1)); // first currency
2095  pItem3->SetAmount(lOtherOfferFinished * (-1)); // other asset
2096  pItem4->SetAmount(lTotalPaidOut); // other currency
2097  }
2098 
2099  if (true == bSuccess) {
2100  // sign the item
2101  pItem1->SignContract(*pServerNym);
2102  pItem2->SignContract(*pServerNym);
2103  pItem3->SignContract(*pServerNym);
2104  pItem4->SignContract(*pServerNym);
2105 
2106  pItem1->SaveContract();
2107  pItem2->SaveContract();
2108  pItem3->SaveContract();
2109  pItem4->SaveContract();
2110 
2111  // the Transaction "owns" the item now and will handle cleaning
2112  // it up.
2113  pTrans1->AddItem(*pItem1);
2114  pTrans2->AddItem(*pItem2);
2115  pTrans3->AddItem(*pItem3);
2116  pTrans4->AddItem(*pItem4);
2117 
2118  pTrans1->SignContract(*pServerNym);
2119  pTrans2->SignContract(*pServerNym);
2120  pTrans3->SignContract(*pServerNym);
2121  pTrans4->SignContract(*pServerNym);
2122 
2123  pTrans1->SaveContract();
2124  pTrans2->SaveContract();
2125  pTrans3->SaveContract();
2126  pTrans4->SaveContract();
2127 
2128  // Here the transactions we just created are actually added to
2129  // the ledgers.
2130  theFirstAssetInbox.AddTransaction(*pTrans1);
2131  theFirstCurrencyInbox.AddTransaction(*pTrans2);
2132  theOtherAssetInbox.AddTransaction(*pTrans3);
2133  theOtherCurrencyInbox.AddTransaction(*pTrans4);
2134 
2135  // Release any signatures that were there before (They won't
2136  // verify anymore anyway, since the content has changed.)
2137  theFirstAssetInbox.ReleaseSignatures();
2138  theFirstCurrencyInbox.ReleaseSignatures();
2139  theOtherAssetInbox.ReleaseSignatures();
2140  theOtherCurrencyInbox.ReleaseSignatures();
2141 
2142  // Sign all four of them.
2143  theFirstAssetInbox.SignContract(*pServerNym);
2144  theFirstCurrencyInbox.SignContract(*pServerNym);
2145  theOtherAssetInbox.SignContract(*pServerNym);
2146  theOtherCurrencyInbox.SignContract(*pServerNym);
2147 
2148  // Save all four of them internally
2149  theFirstAssetInbox.SaveContract();
2150  theFirstCurrencyInbox.SaveContract();
2151  theOtherAssetInbox.SaveContract();
2152  theOtherCurrencyInbox.SaveContract();
2153 
2154  // TODO: Better rollback capabilities in case of failures here:
2155 
2156  // Save the four inboxes to storage. (File, DB, wherever it
2157  // goes.)
2158 
2159  pFirstAssetAcct->SaveInbox(theFirstAssetInbox);
2160  pFirstCurrencyAcct->SaveInbox(theFirstCurrencyInbox);
2161  pOtherAssetAcct->SaveInbox(theOtherAssetInbox);
2162  pOtherCurrencyAcct->SaveInbox(theOtherCurrencyInbox);
2163 
2164  // These correspond to the AddTransaction() calls just above.
2165  // The actual receipts are stored in separate files now.
2166  //
2167  pTrans1->SaveBoxReceipt(theFirstAssetInbox);
2168  pTrans2->SaveBoxReceipt(theFirstCurrencyInbox);
2169  pTrans3->SaveBoxReceipt(theOtherAssetInbox);
2170  pTrans4->SaveBoxReceipt(theOtherCurrencyInbox);
2171 
2172  // Save the four accounts.
2173  pFirstAssetAcct->ReleaseSignatures();
2174  pFirstCurrencyAcct->ReleaseSignatures();
2175  pOtherAssetAcct->ReleaseSignatures();
2176  pOtherCurrencyAcct->ReleaseSignatures();
2177  pFirstAssetAcct->SignContract(*pServerNym);
2178  pFirstCurrencyAcct->SignContract(*pServerNym);
2179  pOtherAssetAcct->SignContract(*pServerNym);
2180  pOtherCurrencyAcct->SignContract(*pServerNym);
2181  pFirstAssetAcct->SaveContract();
2182  pFirstCurrencyAcct->SaveContract();
2183  pOtherAssetAcct->SaveContract();
2184  pOtherCurrencyAcct->SaveContract();
2185  pFirstAssetAcct->SaveAccount();
2186  pFirstCurrencyAcct->SaveAccount();
2187  pOtherAssetAcct->SaveAccount();
2188  pOtherCurrencyAcct->SaveAccount();
2189  }
2190  // If money was short, let's see WHO was short so we can remove his
2191  // trade.
2192  // Also, if money was short, inbox notices only go to the rejectees.
2193  // But if success, then notices go to all four inboxes.
2194  else {
2195  otWarn << "Unable to perform trade in OTMarket::"
2196  << __FUNCTION__ << "\n";
2197 
2198  // Let's figure out which one it was and remove his trade and
2199  // offer.
2200  bool bFirstTraderIsBroke = false, bOtherTraderIsBroke = false;
2201 
2202  // Here's what's going on here:
2203  // "Figure out if this guy was short, or if it was that guy.
2204  // Send a notice
2205  // to the one who got rejected for being short on cash.
2206  //
2207  // Else NEITHER was short, so delete them both with no notice.
2208  //
2209  // (After checking both asset accounts, there's another If
2210  // statement below where
2211  // I repeat this process for the currency accounts as well.)
2212  //
2213  // This whole section occurs because even though the trade and
2214  // offer were valid
2215  // and good to go, at least one of the four accounts was short
2216  // of funds.
2217  //
2218  if (pAssetAccountToDebit->GetBalance() <
2219  lMinIncrementPerRound) {
2220  OTItem* pTempItem = nullptr;
2221  OTTransaction* pTempTransaction = nullptr;
2222  OTLedger* pTempInbox = nullptr;
2223 
2224  if (pAssetAccountToDebit == pFirstAssetAcct) {
2225  pTempItem = pItem1;
2226  bFirstTraderIsBroke = true;
2227  pTempTransaction = pTrans1;
2228  pTempInbox = &theFirstAssetInbox;
2229 
2230  delete pItem3;
2231  pItem3 = nullptr;
2232  delete pTrans3;
2233  pTrans3 = nullptr;
2234  }
2235  else // it's the other asset account
2236  {
2237  pTempItem = pItem3;
2238  bOtherTraderIsBroke = true;
2239  pTempTransaction = pTrans3;
2240  pTempInbox = &theOtherAssetInbox;
2241 
2242  delete pItem1;
2243  pItem1 = nullptr;
2244  delete pTrans1;
2245  pTrans1 = nullptr;
2246  }
2247 
2248  pTempItem->SetStatus(OTItem::rejection);
2249  pTempItem->SignContract(*pServerNym);
2250  pTempItem->SaveContract();
2251 
2252  pTempTransaction->AddItem(*pTempItem);
2253  pTempTransaction->SignContract(*pServerNym);
2254  pTempTransaction->SaveContract();
2255 
2256  pTempInbox->AddTransaction(*pTempTransaction);
2257 
2258  pTempInbox->ReleaseSignatures();
2259  pTempInbox->SignContract(*pServerNym);
2260  pTempInbox->SaveContract();
2261  pTempInbox->SaveInbox();
2262 
2263  pTempTransaction->SaveBoxReceipt(*pTempInbox);
2264  }
2265  else {
2266  delete pItem1;
2267  pItem1 = nullptr;
2268  delete pTrans1;
2269  pTrans1 = nullptr;
2270  delete pItem3;
2271  pItem3 = nullptr;
2272  delete pTrans3;
2273  pTrans3 = nullptr;
2274  }
2275 
2276  // This section is identical to the one above, except for the
2277  // currency accounts.
2278  //
2279  if (pCurrencyAccountToDebit->GetBalance() < lPrice) {
2280  OTItem* pTempItem = nullptr;
2281  OTTransaction* pTempTransaction = nullptr;
2282  OTLedger* pTempInbox = nullptr;
2283 
2284  if (pCurrencyAccountToDebit == pFirstCurrencyAcct) {
2285  pTempItem = pItem2;
2286  bFirstTraderIsBroke = true;
2287  pTempTransaction = pTrans2;
2288  pTempInbox = &theFirstCurrencyInbox;
2289 
2290  delete pItem4;
2291  pItem4 = nullptr;
2292  delete pTrans4;
2293  pTrans4 = nullptr;
2294  }
2295  else // it's the other asset account
2296  {
2297  pTempItem = pItem4;
2298  bOtherTraderIsBroke = true;
2299  pTempTransaction = pTrans4;
2300  pTempInbox = &theOtherCurrencyInbox;
2301 
2302  delete pItem2;
2303  pItem2 = nullptr;
2304  delete pTrans2;
2305  pTrans2 = nullptr;
2306  }
2307 
2308  pTempItem->SetStatus(OTItem::rejection);
2309  pTempItem->SignContract(*pServerNym);
2310  pTempItem->SaveContract();
2311 
2312  pTempTransaction->AddItem(*pTempItem);
2313  pTempTransaction->SignContract(*pServerNym);
2314  pTempTransaction->SaveContract();
2315 
2316  pTempInbox->AddTransaction(*pTempTransaction);
2317 
2318  pTempInbox->ReleaseSignatures();
2319  pTempInbox->SignContract(*pServerNym);
2320  pTempInbox->SaveContract();
2321  pTempInbox->SaveInbox();
2322 
2323  pTempTransaction->SaveBoxReceipt(*pTempInbox);
2324  }
2325  else {
2326  delete pItem2;
2327  pItem2 = nullptr;
2328  delete pTrans2;
2329  pTrans2 = nullptr;
2330  delete pItem4;
2331  pItem4 = nullptr;
2332  delete pTrans4;
2333  pTrans4 = nullptr;
2334  }
2335 
2336  // If either trader is broke, we flag the trade for removal.
2337  // No other trades will process against it and it will be
2338  // removed from market soon.
2339  //
2340  if (bFirstTraderIsBroke) theTrade.FlagForRemoval();
2341  if (bOtherTraderIsBroke) pOtherTrade->FlagForRemoval();
2342 
2343  } // success == false
2344  } // all four boxes were successfully loaded or generated.
2345  } // "this entire function can be divided..."
2346 
2347  cleanup_four_accounts(pFirstAssetAcct, pFirstCurrencyAcct, pOtherAssetAcct,
2348  pOtherCurrencyAcct);
2349 }
EXPORT Storable * CreateObject(StoredObjectType eType)
Definition: OTStorage.cpp:530
static EXPORT OTTransaction * GenerateTransaction(const OTIdentifier &theUserID, const OTIdentifier &theAccountID, const OTIdentifier &theServerID, transactionType theType, int64_t lTransactionNum=0)
static EXPORT OTCronItem * LoadCronReceipt(const int64_t &lTransactionNum)
Definition: OTCronItem.cpp:227
OTLOG_IMPORT OTLogStream otOut
const OTIdentifier & GetCurrencyID() const
Definition: OTMarket.hpp:268
int64_t time64_t
Definition: Common.hpp:209
const OTIdentifier & GetAssetID() const
Definition: OTMarket.hpp:264
static EXPORT OTItem * CreateItemFromTransaction(const OTTransaction &theOwner, OTItem::itemType theType, const OTIdentifier *pDestinationAcctID=nullptr)
Definition: OTItem.cpp:1451
time64_t OTTimeGetCurrentTime()
Definition: Common.hpp:211
#define OT_ASSERT(x)
Definition: Assert.hpp:150
#define OT_ASSERT_MSG(x, s)
Definition: Assert.hpp:155
void cleanup_four_accounts(OTAccount *p1, OTAccount *p2, OTAccount *p3, OTAccount *p4)
Definition: OTMarket.cpp:964
void rollback_four_accounts(OTAccount &p1, bool b1, const int64_t &a1, OTAccount &p2, bool b2, const int64_t &a2, OTAccount &p3, bool b3, const int64_t &a3, OTAccount &p4, bool b4, const int64_t &a4)
Definition: OTMarket.cpp:986
OTLOG_IMPORT OTLogStream otWarn
const int64_t & GetScale() const
Definition: OTMarket.hpp:277
OTLOG_IMPORT OTLogStream otErr
#define MAX_MARKET_QUERY_DEPTH
Definition: OTMarket.hpp:149
static EXPORT OTAccount * LoadExistingAccount(const OTIdentifier &accountId, const OTIdentifier &serverId)
Definition: OTAccount.cpp:480
OTLOG_IMPORT OTLogStream otLog5
bool opentxs::OTMarket::ProcessTrade ( OTTrade theTrade,
OTOffer theOffer 
)

Definition at line 2427 of file OTMarket.cpp.

2428 {
2429  if (theOffer.GetAmountAvailable() < theOffer.GetMinimumIncrement()) {
2430  otOut << "OTMarket::" << __FUNCTION__ << ": Removing offer from "
2431  "market. (Amount Available is "
2432  "less than Min Increment.)\n";
2433  return false;
2434  }
2435 
2436  int64_t lRelevantPrice = 0;
2437 
2438  // If I'm trying to SELL something, then I care about the highest bidder.
2439  if (theOffer.IsAsk()) lRelevantPrice = GetHighestBidPrice();
2440 
2441  // ...But if I'm trying to BUY something, then I care about the lowest ask
2442  // price.
2443  else
2444  lRelevantPrice = GetLowestAskPrice();
2445 
2446  // If there were no relevant offers (or if they were all market orders, aka:
2447  // 0 == lRelevantPrice),
2448  // AND if *I* am a market order, then REMOVE me from the market. (Market
2449  // orders only process once,
2450  // so if we are processing here and we haven't found any potential matches,
2451  // we're REMOVED.)
2452  //
2453  if ((0 == lRelevantPrice) &&
2454  theOffer.IsMarketOrder()) // Market order has 0 price.
2455  return false;
2456 
2457  // If there were no bids/asks (whichever is relevant to this trade) on the
2458  // market at ALL,
2459  // then lRelevant price will be 0 at this point. In which case we're DONE.
2460  //
2461  // If I'm selling, and the highest bid is less than my price limit, then
2462  // we're DONE.
2463  // If I'm buying, and the lowest ask price is higher than my price limit,
2464  // then we're DONE.
2465  //
2466  if ((0 == lRelevantPrice) || // If 0, we KNOW we're not a market order,
2467  // since we would have returned already
2468  // (above.)
2469 
2470  // We only care about price-based restrictions if we're a limit order.
2471  // (Market orders don't care about price, so they skip this condition.)
2472  (theOffer.IsLimitOrder() &&
2473  ((theOffer.IsAsk() && (lRelevantPrice < theOffer.GetPriceLimit())) ||
2474  (theOffer.IsBid() && (lRelevantPrice > theOffer.GetPriceLimit()))))) {
2475  // There is nothing on the market currently within my price limits.
2476  // We're DONE. (For now.)
2477  return true;
2478  }
2479 
2480  // If I got this far, that means there ARE bidders or sellers (whichever the
2481  // current trade cares about)
2482  // in the market WITHIN THIS TRADE'S PRICE LIMITS. So we're going to go up
2483  // the list of what's available, and trade.
2484 
2485  if (theOffer.IsAsk()) // If I'm selling,
2486  {
2487  // rbegin puts us on the upper bound of the highest bidder (any new
2488  // bidders at the same price would
2489  // be added at the lower bound, where they are last in line.) The upper
2490  // bound, on the other hand, is
2491  // first in line. So we start there, and loop backwards until there are
2492  // no other bids within my price range.
2493  for (mapOfOffers::reverse_iterator rr = m_mapBids.rbegin();
2494  rr != m_mapBids.rend(); ++rr) {
2495  // then I want to start at the highest bidder and loop DOWN until
2496  // hitting my price limit.
2497  OTOffer* pBid = rr->second;
2498  OT_ASSERT(nullptr != pBid);
2499 
2500  // NOTE: Market orders only process once, and they are processed in
2501  // the order they were added to the market.
2502  //
2503  // If BOTH offers are market orders, we just skip this bid.
2504  //
2505  // But FURTHERMORE: We ONLY process a market order as theOffer, not
2506  // as pBid!
2507  // Imagine if pBid is a market order and theOffer isn't -- that
2508  // would mean pBid
2509  // hasn't been processed yet (since it will only process once.) So
2510  // it needs to
2511  // wait its turn! It will get its one shot WHEN ITS TURN comes.
2512  //
2513  if (pBid->IsMarketOrder())
2514  // if (theOffer.IsMarketOrder() &&
2515  // pBid->IsMarketOrder())
2516  // continue;
2517  break;
2518  // NOTE: Why break, instead of continue? Because since we are
2519  // looping through the bids,
2520  // from the HIGHEST down to the LOWEST, and since market orders have
2521  // a ZERO price, we know
2522  // for a fact that there are not any other non-zero bids. (So we
2523  // might as well break.)
2524 
2525  // I'm selling.
2526  //
2527  // If the bid is larger than, or equal to, my low-side-limit,
2528  // and the amount available is at least my minimum increment, (and
2529  // vice versa),
2530  // ...then let's trade!
2531  //
2532  if (theOffer.IsMarketOrder() || // If I don't care about price...
2533  (pBid->GetPriceLimit() >=
2534  theOffer.GetPriceLimit())) // Or if this bid is within my price
2535  // range...
2536  {
2537  // Notice the above "if" is ONLY based on price... because the
2538  // "else" returns!
2539  // (Once I am out of my price range, no point to continue
2540  // looping.)
2541  //
2542  // ...So all the other "if"s have to go INSIDE the block here:
2543  //
2544  if ((pBid->GetAmountAvailable() >=
2545  theOffer.GetMinimumIncrement()) &&
2546  (theOffer.GetAmountAvailable() >=
2547  pBid->GetMinimumIncrement()) &&
2548  (nullptr != pBid->GetTrade()) &&
2549  !pBid->GetTrade()->IsFlaggedForRemoval())
2550 
2551  ProcessTrade(theTrade, theOffer, *pBid); // <========
2552  }
2553 
2554  // Else, the bid is lower than I am willing to sell. (And all the
2555  // remaining bids are even lower.)
2556  //
2557  else if (theOffer.IsLimitOrder()) {
2558  pBid = nullptr;
2559  return true; // stay on cron for more processing (for now.)
2560  }
2561 
2562  // The offer has no more trading to do--it's done.
2563  if (theTrade.IsFlaggedForRemoval() || // during processing, the
2564  // trade may have gotten
2565  // flagged.
2566  (theOffer.GetMinimumIncrement() >
2567  theOffer.GetAmountAvailable()))
2568  return false; // remove this trade from cron
2569 
2570  pBid = nullptr;
2571  }
2572  }
2573  // I'm buying
2574  else {
2575  // Begin puts us on the lower bound of the lowest seller (any new
2576  // sellers at the same price would
2577  // be added at the upper bound for that price, where they are last in
2578  // line.) The lower bound, on the other hand, is
2579  // first in line. So we start there, and loop forwards until there are
2580  // no other asks within my price range.
2581  //
2582  for (auto& it : m_mapAsks) {
2583  // then I want to start at the lowest seller and loop UP until
2584  // hitting my price limit.
2585  OTOffer* pAsk = it.second;
2586  OT_ASSERT(nullptr != pAsk);
2587 
2588  // NOTE: Market orders only process once, and they are processed in
2589  // the order they were added to the market.
2590  //
2591  // If BOTH offers are market orders, we just skip this ask.
2592  //
2593  // But FURTHERMORE: We ONLY process a market order as theOffer, not
2594  // as pAsk!
2595  // Imagine if pAsk is a market order and theOffer isn't -- that
2596  // would mean pAsk
2597  // hasn't been processed yet (since it will only process once.) So
2598  // it needs to
2599  // wait its turn! It will get its one shot WHEN ITS TURN comes.
2600  //
2601  if (pAsk->IsMarketOrder())
2602  // if (theOffer.IsMarketOrder() &&
2603  // pAsk->IsMarketOrder())
2604  continue;
2605 
2606  // I'm buying.
2607  // If the ask price is less than, or equal to, my price limit,
2608  // and the amount available for purchase is at least my minimum
2609  // increment, (and vice versa),
2610  // ...then let's trade!
2611  //
2612  if (theOffer.IsMarketOrder() || // If I don't care about price...
2613  (pAsk->GetPriceLimit() <=
2614  theOffer.GetPriceLimit())) // Or if this ask is within my price
2615  // range...
2616  {
2617  // Notice the above "if" is ONLY based on price... because the
2618  // "else" returns!
2619  // (Once I am out of my price range, no point to continue
2620  // looping.)
2621  // So all the other "if"s have to go INSIDE the block here:
2622  //
2623  if ((pAsk->GetAmountAvailable() >=
2624  theOffer.GetMinimumIncrement()) &&
2625  (theOffer.GetAmountAvailable() >=
2626  pAsk->GetMinimumIncrement()) &&
2627  (nullptr != pAsk->GetTrade()) &&
2628  !pAsk->GetTrade()->IsFlaggedForRemoval())
2629 
2630  ProcessTrade(theTrade, theOffer, *pAsk); // <=======
2631  }
2632  // Else, the ask price is higher than I am willing to pay. (And all
2633  // the remaining sellers are even HIGHER.)
2634  else if (theOffer.IsLimitOrder()) {
2635  pAsk = nullptr;
2636  return true; // stay on the market for now.
2637  }
2638 
2639  // The offer has no more trading to do--it's done.
2640  if (theTrade.IsFlaggedForRemoval() || // during processing, the
2641  // trade may have gotten
2642  // flagged.
2643  (theOffer.GetMinimumIncrement() >
2644  theOffer.GetAmountAvailable()))
2645  return false; // remove this trade from the market.
2646 
2647  pAsk = nullptr;
2648  }
2649  }
2650 
2651  // Market orders only process once.
2652  // (So tell the caller to remove it.)
2653  //
2654  if (theOffer.IsMarketOrder()) return false; // remove from market.
2655 
2656  return true; // stay on the market for now.
2657 }
OTLOG_IMPORT OTLogStream otOut
int64_t GetLowestAskPrice()
Definition: OTMarket.cpp:934
#define OT_ASSERT(x)
Definition: Assert.hpp:150
void ProcessTrade(OTTrade &theTrade, OTOffer &theOffer, OTOffer &theOtherOffer)
Definition: OTMarket.cpp:1023
int64_t GetHighestBidPrice()
Definition: OTMarket.cpp:919
int32_t opentxs::OTMarket::ProcessXMLNode ( irr::io::IrrXMLReader *&  xml)
virtual

Reimplemented from opentxs::OTContract.

Definition at line 153 of file OTMarket.cpp.

154 {
155  int32_t nReturnVal = 0;
156 
157  // Here we call the parent class first.
158  // If the node is found there, or there is some error,
159  // then we just return either way. But if it comes back
160  // as '0', then nothing happened, and we'll continue executing.
161  //
162  // -- Note you can choose not to call the parent if
163  // you don't want to use any of those xml tags.
164  // As I do below, in the case of OTAccount.
165  // if (nReturnVal = OTContract::ProcessXMLNode(xml))
166  // return nReturnVal;
167 
168  if (!strcmp("market", xml->getNodeName())) {
169  m_strVersion = xml->getAttributeValue("version");
170  SetScale(atol(xml->getAttributeValue("marketScale")));
171  m_lLastSalePrice = atol(xml->getAttributeValue("lastSalePrice"));
172  m_strLastSaleDate = xml->getAttributeValue("lastSaleDate");
173 
174  const OTString strServerID(xml->getAttributeValue("serverID")),
175  strAssetTypeID(xml->getAttributeValue("assetTypeID")),
176  strCurrencyTypeID(xml->getAttributeValue("currencyTypeID"));
177 
178  m_SERVER_ID.SetString(strServerID);
179  m_ASSET_TYPE_ID.SetString(strAssetTypeID);
180  m_CURRENCY_TYPE_ID.SetString(strCurrencyTypeID);
181 
182  otOut << "\n\nMarket. Scale: " << m_lScale << "\n";
183 
184  otWarn << " assetTypeID: " << strAssetTypeID
185  << "\n"
186  " currencyTypeID: " << strCurrencyTypeID
187  << "\n"
188  " ServerID: " << strServerID << "\n";
189 
190  nReturnVal = 1;
191  }
192  else if (!strcmp("offer", xml->getNodeName())) {
193  const OTString strDateAdded(xml->getAttributeValue("dateAdded"));
194  const int64_t lDateAdded =
195  strDateAdded.Exists() ? strDateAdded.ToLong() : 0;
196  const time64_t tDateAdded = OTTimeGetTimeFromSeconds(lDateAdded);
197 
198  OTString strData;
199 
200  if (!OTContract::LoadEncodedTextField(xml, strData) ||
201  !strData.Exists()) {
202  otErr << "Error in OTMarket::" << __FUNCTION__
203  << ": offer field without value.\n";
204  return (-1); // error condition
205  }
206  else {
207  OTOffer* pOffer = new OTOffer(m_SERVER_ID, m_ASSET_TYPE_ID,
208  m_CURRENCY_TYPE_ID, m_lScale);
209 
210  OT_ASSERT(nullptr != pOffer);
211 
212  if (pOffer->LoadContractFromString(strData) &&
213  AddOffer(nullptr, *pOffer, false, tDateAdded)) // bSaveMarket =
214  // false (Don't SAVE
215  // -- we're loading
216  // right now!)
217  {
218  otWarn << "Successfully loaded offer and added to market.\n";
219  }
220  else {
221  otErr << "Error adding offer to market while loading market.\n";
222  delete pOffer;
223  pOffer = nullptr;
224  return (-1);
225  }
226  }
227 
228  nReturnVal = 1;
229  }
230 
231  return nReturnVal;
232 }
bool AddOffer(OTTrade *pTrade, OTOffer &theOffer, bool bSaveFile=true, time64_t tDateAddedToMarket=OT_TIME_ZERO)
Definition: OTMarket.cpp:740
OTLOG_IMPORT OTLogStream otOut
time64_t OTTimeGetTimeFromSeconds(int64_t seconds)
Definition: Common.hpp:215
int64_t time64_t
Definition: Common.hpp:209
EXPORT void SetString(const char *szString)
static EXPORT bool LoadEncodedTextField(irr::io::IrrXMLReader *&xml, OTASCIIArmor &ascOutput)
#define OT_ASSERT(x)
Definition: Assert.hpp:150
OTLOG_IMPORT OTLogStream otWarn
OTLOG_IMPORT OTLogStream otErr
void SetScale(const int64_t &lScale)
Definition: OTMarket.hpp:281
void opentxs::OTMarket::Release ( void  )
virtual

Reimplemented from opentxs::OTContract.

Definition at line 2818 of file OTMarket.cpp.

2819 {
2820  Release_Market(); // since I've overridden the base class, I call it now...
2821 
2822  ot_super::Release(); // since I've overridden the base class, I call it
2823  // now...
2824 
2825  // Then I call this to re-initialize everything (just out of convenience.
2826  // Not always the right move.)
2827  InitMarket();
2828 }
virtual EXPORT void Release()
Definition: OTContract.cpp:277
void opentxs::OTMarket::Release_Market ( )

Definition at line 2790 of file OTMarket.cpp.

2791 {
2792  m_ASSET_TYPE_ID.Release();
2793  m_CURRENCY_TYPE_ID.Release();
2794 
2795  m_SERVER_ID.Release();
2796 
2797  // Elements of this list are cleaned up automatically.
2798  if (nullptr != m_pTradeList) {
2799  delete m_pTradeList;
2800  m_pTradeList = nullptr;
2801  }
2802 
2803  // If there were any dynamically allocated objects, clean them up here.
2804  while (!m_mapBids.empty()) {
2805  OTOffer* pOffer = m_mapBids.begin()->second;
2806  m_mapBids.erase(m_mapBids.begin());
2807  delete pOffer;
2808  pOffer = nullptr;
2809  }
2810  while (!m_mapAsks.empty()) {
2811  OTOffer* pOffer = m_mapAsks.begin()->second;
2812  m_mapAsks.erase(m_mapAsks.begin());
2813  delete pOffer;
2814  pOffer = nullptr;
2815  }
2816 }
virtual EXPORT void Release()
Definition: OTData.cpp:257
bool opentxs::OTMarket::RemoveOffer ( const int64_t &  lTransactionNum)

Definition at line 649 of file OTMarket.cpp.

651 {
652  bool bReturnValue = false;
653 
654  // See if there's something there with that transaction number.
655  auto it = m_mapOffers.find(lTransactionNum);
656 
657  // If it's not already on the list, then there's nothing to remove.
658  if (it == m_mapOffers.end()) {
659  otErr << "Attempt to remove non-existent Offer from Market. "
660  "Transaction #: " << lTransactionNum << "\n";
661  return false;
662  }
663  // Otherwise, if it WAS already there, remove it properly.
664  else {
665  OTOffer* pOffer = it->second;
666 
667  OT_ASSERT(nullptr != pOffer);
668 
669  // This removes it from one list (the one indexed by transaction
670  // number.)
671  // But it's still on one of the other lists...
672  m_mapOffers.erase(it);
673 
674  // The code operates the same whether ask or bid. Just use a pointer.
675  mapOfOffers* pMap = (pOffer->IsBid() ? &m_mapBids : &m_mapAsks);
676 
677  // Future solution here: instead of looping through all the offers and
678  // finding it,
679  // I could just have each Offer store a copy of the iterator that's
680  // returned when
681  // it's first inserted. Later I just erase that iterator from the right
682  // list to remove. Todo.
683  OTOffer* pSameOffer = nullptr;
684 
685  for (mapOfOffers::iterator iii = pMap->begin(); iii != pMap->end();
686  ++iii) {
687  pSameOffer = iii->second;
688 
689  OT_ASSERT_MSG(nullptr != pSameOffer,
690  "nullptr offer pointer in OTMarket::RemoveOffer.\n");
691 
692  // found it!
693  if (lTransactionNum == pSameOffer->GetTransactionNum()) {
694  pMap->erase(iii);
695  break;
696  }
697 
698  // Later on, below this loop, this pointer will be nullptr or not,
699  // and
700  // I will know if it was removed.
701  pSameOffer = nullptr;
702  }
703 
704  if (nullptr == pSameOffer) {
705  otErr << "Removed Offer from offers list, but not found on bid/ask "
706  "list.\n";
707  }
708  else // This means it was found and removed from the second list as
709  // well.
710  {
711  bReturnValue = true; // Success.
712  }
713 
714  // pOffer was found on the Offers list.
715  // pSameOffer was found on the Bid or Ask list, with the same
716  // transaction ID.
717  // They SHOULD be pointers to the SAME object.
718  // Therefore I CANNOT delete them both.
719  //
720  OT_ASSERT(pOffer == pSameOffer);
721 
722  delete pOffer;
723  pOffer = nullptr;
724  pSameOffer = nullptr;
725  }
726 
727  if (bReturnValue)
728  return SaveMarket(); // <====== SAVE since an offer was removed.
729  else
730  return false;
731 }
std::multimap< int64_t, OTOffer * > mapOfOffers
Definition: OTMarket.hpp:156
#define OT_ASSERT(x)
Definition: Assert.hpp:150
#define OT_ASSERT_MSG(x, s)
Definition: Assert.hpp:155
OTLOG_IMPORT OTLogStream otErr
bool opentxs::OTMarket::SaveContractWallet ( std::ofstream &  ofs) const
virtual

Implements opentxs::OTContract.

Definition at line 2830 of file OTMarket.cpp.

2831 {
2832  return true;
2833 }
bool opentxs::OTMarket::SaveMarket ( )

Definition at line 857 of file OTMarket.cpp.

858 {
859  OT_ASSERT(nullptr != GetCron());
860  OT_ASSERT(nullptr != GetCron()->GetServerNym());
861 
862  OTIdentifier MARKET_ID(*this);
863  OTString str_MARKET_ID(MARKET_ID);
864 
865  const char* szFoldername = OTFolders::Market().Get();
866  const char* szFilename = str_MARKET_ID.Get();
867 
868  // Remember, if the market has changed, the new contents will not be written
869  // anywhere
870  // until that market has been signed. So I have to re-sign here, or it would
871  // just save
872  // the old version of the market from before the most recent changes.
874 
875  // Sign it, save it internally to string, and then save that out to the
876  // file.
877  if (!SignContract(*(GetCron()->GetServerNym())) || !SaveContract() ||
878  !SaveContract(szFoldername, szFilename)) {
879  otErr << "Error saving Market:\n" << szFoldername
880  << OTLog::PathSeparator() << szFilename << "\n";
881  return false;
882  }
883 
884  // Save a copy of recent trades.
885 
886  if (nullptr != m_pTradeList) {
887  const char* szSubFolder = "recent"; // todo stop hardcoding.
888 
889  // If this fails, oh well. It's informational, anyway.
890  if (!OTDB::StoreObject(*m_pTradeList, szFoldername, // markets
891  szSubFolder, // markets/recent
892  szFilename)) // markets/recent/Market_ID
893  otErr << "Error saving recent trades for Market:\n" << szFoldername
894  << OTLog::PathSeparator() << szSubFolder
895  << OTLog::PathSeparator() << szFilename << "\n";
896  }
897 
898  return true;
899 }
EXPORT bool SaveContract()
static EXPORT const char * PathSeparator()
Definition: OTLog.cpp:408
EXPORT void ReleaseSignatures()
Definition: OTContract.cpp:989
#define OT_ASSERT(x)
Definition: Assert.hpp:150
static EXPORT const OTString & Market()
Definition: OTFolders.cpp:319
EXPORT const char * Get() const
Definition: OTString.cpp:1045
virtual EXPORT bool SignContract(const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr)
Definition: OTContract.cpp:484
OTLOG_IMPORT OTLogStream otErr
EXPORT bool StoreObject(Storable &theContents, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:759
OTCron * GetCron()
Definition: OTMarket.hpp:317
void opentxs::OTMarket::SetAssetID ( const OTIdentifier ASSET_ID)
inline

Definition at line 251 of file OTMarket.hpp.

252  {
253  m_ASSET_TYPE_ID = ASSET_ID;
254  }
void opentxs::OTMarket::SetCronPointer ( OTCron theCron)
inline

Definition at line 313 of file OTMarket.hpp.

314  {
315  m_pCron = &theCron;
316  }
void opentxs::OTMarket::SetCurrencyID ( const OTIdentifier CURRENCY_ID)
inline

Definition at line 255 of file OTMarket.hpp.

256  {
257  m_CURRENCY_TYPE_ID = CURRENCY_ID;
258  }
void opentxs::OTMarket::SetLastSalePrice ( const int64_t &  lLastSalePrice)
inline

Definition at line 292 of file OTMarket.hpp.

293  {
294  m_lLastSalePrice = lLastSalePrice;
295  if (m_lLastSalePrice < 1) m_lLastSalePrice = 1;
296  }
void opentxs::OTMarket::SetScale ( const int64_t &  lScale)
inline

Definition at line 281 of file OTMarket.hpp.

282  {
283  m_lScale = lScale;
284  if (m_lScale < 1) m_lScale = 1;
285  }
void opentxs::OTMarket::SetServerID ( const OTIdentifier SERVER_ID)
inline

Definition at line 259 of file OTMarket.hpp.

260  {
261  m_SERVER_ID = SERVER_ID;
262  }
void opentxs::OTMarket::UpdateContents ( )
virtual

Reimplemented from opentxs::OTContract.

Definition at line 234 of file OTMarket.cpp.

235 {
236  // I release this because I'm about to repopulate it.
238 
239  m_xmlUnsigned.Concatenate("<?xml version=\"%s\"?>\n\n", "1.0");
240 
241  const OTString SERVER_ID(m_SERVER_ID), ASSET_TYPE_ID(m_ASSET_TYPE_ID),
242  CURRENCY_TYPE_ID(m_CURRENCY_TYPE_ID);
243 
244  m_xmlUnsigned.Concatenate("<market\n version=\"%s\"\n"
245  " serverID=\"%s\"\n"
246  " assetTypeID=\"%s\"\n"
247  " currencyTypeID=\"%s\"\n"
248  " marketScale=\"%lld\"\n"
249  " lastSaleDate=\"%s\"\n"
250  " lastSalePrice=\"%lld\""
251  " >\n\n",
252  m_strVersion.Get(), SERVER_ID.Get(),
253  ASSET_TYPE_ID.Get(), CURRENCY_TYPE_ID.Get(),
254  m_lScale, m_strLastSaleDate.c_str(),
255  m_lLastSalePrice);
256 
257  // Save the offers for sale.
258  for (auto& it : m_mapAsks) {
259  OTOffer* pOffer = it.second;
260  OT_ASSERT(nullptr != pOffer);
261 
262  OTString strOffer(
263  *pOffer); // Extract the offer contract into string form.
264  OTASCIIArmor ascOffer(strOffer); // Base64-encode that for storage.
265 
266  int64_t lDateAddedToMarket =
267  OTTimeGetSecondsFromTime(pOffer->GetDateAddedToMarket());
268 
269  m_xmlUnsigned.Concatenate("<offer dateAdded=\"%" PRId64
270  "\">\n%s</offer>\n\n",
271  lDateAddedToMarket, ascOffer.Get());
272  }
273 
274  // Save the bids.
275  for (auto& it : m_mapBids) {
276  OTOffer* pOffer = it.second;
277  OT_ASSERT(nullptr != pOffer);
278 
279  OTString strOffer(
280  *pOffer); // Extract the offer contract into string form.
281  OTASCIIArmor ascOffer(strOffer); // Base64-encode that for storage.
282 
283  int64_t lDateAddedToMarket =
284  OTTimeGetSecondsFromTime(pOffer->GetDateAddedToMarket());
285 
286  m_xmlUnsigned.Concatenate("<offer dateAdded=\"%" PRId64
287  "\">\n%s</offer>\n\n",
288  lDateAddedToMarket, ascOffer.Get());
289  }
290 
291  m_xmlUnsigned.Concatenate("</market>\n");
292 }
EXPORT void Concatenate(const char *arg,...)
Definition: OTString.cpp:1334
#define OT_ASSERT(x)
Definition: Assert.hpp:150
OTStringXML m_xmlUnsigned
Definition: OTContract.hpp:174
EXPORT const char * Get() const
Definition: OTString.cpp:1045
int64_t OTTimeGetSecondsFromTime(time64_t time)
Definition: Common.hpp:230
virtual EXPORT void Release()
Definition: OTString.cpp:765
bool opentxs::OTMarket::ValidateOfferForMarket ( OTOffer theOffer,
OTString pReason = nullptr 
)

Definition at line 2660 of file OTMarket.cpp.

2661 {
2662  bool bValidOffer = true;
2663  OTString strReason("");
2664 
2665  if (GetServerID() != theOffer.GetServerID()) {
2666  bValidOffer = false;
2667  const OTString strID(GetServerID()), strOtherID(theOffer.GetServerID());
2668  strReason.Format("Wrong Server ID on offer. Expected %s, but found %s",
2669  strID.Get(), strOtherID.Get());
2670  }
2671  else if (GetAssetID() != theOffer.GetAssetID()) {
2672  bValidOffer = false;
2673  const OTString strID(GetAssetID()), strOtherID(theOffer.GetAssetID());
2674  strReason.Format("Wrong Asset ID on offer. Expected %s, but found %s",
2675  strID.Get(), strOtherID.Get());
2676  }
2677  else if (GetCurrencyID() != theOffer.GetCurrencyID()) {
2678  bValidOffer = false;
2679  const OTString strID(GetCurrencyID()),
2680  strOtherID(theOffer.GetCurrencyID());
2681  strReason.Format(
2682  "Wrong Currency ID on offer. Expected %s, but found %s",
2683  strID.Get(), strOtherID.Get());
2684  }
2685  else if (GetScale() != theOffer.GetScale()) {
2686  bValidOffer = false;
2687  strReason.Format(
2688  "Wrong Market Scale on offer. Expected %lld, but found %lld",
2689  GetScale(), theOffer.GetScale());
2690  }
2691 
2692  // The above four items must match in order for it to even be the same
2693  // MARKET.
2694  else if (theOffer.GetMinimumIncrement() <= 0) {
2695  bValidOffer = false;
2696  strReason.Format("Minimum Increment on offer is <= 0: %lld",
2697  theOffer.GetMinimumIncrement());
2698  }
2699  else if (theOffer.GetMinimumIncrement() < GetScale()) {
2700  bValidOffer = false;
2701  strReason.Format("Minimum Increment on offer (%lld) is less than "
2702  "market scale (%lld).",
2703  theOffer.GetMinimumIncrement(), GetScale());
2704  }
2705  else if ((theOffer.GetMinimumIncrement() % GetScale()) != 0) {
2706  bValidOffer = false;
2707  strReason.Format("Minimum Increment on offer (%lld) Mod market scale "
2708  "(%lld) is not equal to zero.",
2709  theOffer.GetMinimumIncrement(), GetScale());
2710  }
2711  else if (theOffer.GetMinimumIncrement() > theOffer.GetAmountAvailable()) {
2712  bValidOffer = false;
2713  strReason.Format(
2714  "Minimum Increment on offer (%lld) is more than the amount of "
2715  "assets available for trade on that same offer (%lld).",
2716  theOffer.GetMinimumIncrement(), theOffer.GetAmountAvailable());
2717  }
2718 
2719  if (bValidOffer)
2720  otLog4 << "Offer is valid for market.\n";
2721  else {
2722  otOut << __FUNCTION__
2723  << ": Offer is invalid for this market: " << strReason << "\n";
2724 
2725  if (nullptr != pReason) *pReason = strReason;
2726  }
2727 
2728  return bValidOffer;
2729 }
OTLOG_IMPORT OTLogStream otLog4
OTLOG_IMPORT OTLogStream otOut
const OTIdentifier & GetCurrencyID() const
Definition: OTMarket.hpp:268
const OTIdentifier & GetServerID() const
Definition: OTMarket.hpp:272
const OTIdentifier & GetAssetID() const
Definition: OTMarket.hpp:264
const int64_t & GetScale() const
Definition: OTMarket.hpp:277

The documentation for this class was generated from the following files: