Open-Transactions  0.93.0-ge03d287
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
OTClient.cpp
Go to the documentation of this file.
1 /************************************************************
2 *
3 * OTClient.hpp
4 *
5 */
6 
7 /************************************************************
8  -----BEGIN PGP SIGNED MESSAGE-----
9  Hash: SHA1
10 
11  * OPEN TRANSACTIONS
12  *
13  * Financial Cryptography and Digital Cash
14  * Library, Protocol, API, Server, CLI, GUI
15  *
16  * -- Anonymous Numbered Accounts.
17  * -- Untraceable Digital Cash.
18  * -- Triple-Signed Receipts.
19  * -- Cheques, Vouchers, Transfers, Inboxes.
20  * -- Basket Currencies, Markets, Payment Plans.
21  * -- Signed, XML, Ricardian-style Contracts.
22  * -- Scripted smart contracts.
23  *
24  * Copyright (C) 2010-2013 by "Fellow Traveler" (A pseudonym)
25  *
26  * EMAIL:
28  *
29  * BITCOIN: 1NtTPVVjDsUfDWybS4BwvHpG2pdS9RnYyQ
30  *
31  * KEY FINGERPRINT (PGP Key in license file):
32  * 9DD5 90EB 9292 4B48 0484 7910 0308 00ED F951 BB8E
33  *
34  * OFFICIAL PROJECT WIKI(s):
35  * https://github.com/FellowTraveler/Moneychanger
36  * https://github.com/FellowTraveler/Open-Transactions/wiki
37  *
38  * WEBSITE:
39  * http://www.OpenTransactions.org/
40  *
41  * Components and licensing:
42  * -- Moneychanger..A Java client GUI.....LICENSE:.....GPLv3
43  * -- otlib.........A class library.......LICENSE:...LAGPLv3
44  * -- otapi.........A client API..........LICENSE:...LAGPLv3
45  * -- opentxs/ot....Command-line client...LICENSE:...LAGPLv3
46  * -- otserver......Server Application....LICENSE:....AGPLv3
47  * Github.com/FellowTraveler/Open-Transactions/wiki/Components
48  *
49  * All of the above OT components were designed and written by
50  * Fellow Traveler, with the exception of Moneychanger, which
51  * was contracted out to Vicky C ([email protected]).
52  * The open-source community has since actively contributed.
53  *
54  * -----------------------------------------------------
55  *
56  * LICENSE:
57  * This program is free software: you can redistribute it
58  * and/or modify it under the terms of the GNU Affero
59  * General Public License as published by the Free Software
60  * Foundation, either version 3 of the License, or (at your
61  * option) any later version.
62  *
63  * ADDITIONAL PERMISSION under the GNU Affero GPL version 3
64  * section 7: (This paragraph applies only to the LAGPLv3
65  * components listed above.) If you modify this Program, or
66  * any covered work, by linking or combining it with other
67  * code, such other code is not for that reason alone subject
68  * to any of the requirements of the GNU Affero GPL version 3.
69  * (==> This means if you are only using the OT API, then you
70  * don't have to open-source your code--only your changes to
71  * Open-Transactions itself must be open source. Similar to
72  * LGPLv3, except it applies to software-as-a-service, not
73  * just to distributing binaries.)
74  *
75  * Extra WAIVER for OpenSSL, Lucre, and all other libraries
76  * used by Open Transactions: This program is released under
77  * the AGPL with the additional exemption that compiling,
78  * linking, and/or using OpenSSL is allowed. The same is true
79  * for any other open source libraries included in this
80  * project: complete waiver from the AGPL is hereby granted to
81  * compile, link, and/or use them with Open-Transactions,
82  * according to their own terms, as long as the rest of the
83  * Open-Transactions terms remain respected, with regard to
84  * the Open-Transactions code itself.
85  *
86  * Lucre License:
87  * This code is also "dual-license", meaning that Ben Lau-
88  * rie's license must also be included and respected, since
89  * the code for Lucre is also included with Open Transactions.
90  * See Open-Transactions/src/otlib/lucre/LUCRE_LICENSE.txt
91  * The Laurie requirements are light, but if there is any
92  * problem with his license, simply remove the Lucre code.
93  * Although there are no other blind token algorithms in Open
94  * Transactions (yet. credlib is coming), the other functions
95  * will continue to operate.
96  * See Lucre on Github: https://github.com/benlaurie/lucre
97  * -----------------------------------------------------
98  * You should have received a copy of the GNU Affero General
99  * Public License along with this program. If not, see:
100  * http://www.gnu.org/licenses/
101  *
102  * If you would like to use this software outside of the free
103  * software license, please contact FellowTraveler.
104  * (Unfortunately many will run anonymously and untraceably,
105  * so who could really stop them?)
106  *
107  * DISCLAIMER:
108  * This program is distributed in the hope that it will be
109  * useful, but WITHOUT ANY WARRANTY; without even the implied
110  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
111  * PURPOSE. See the GNU Affero General Public License for
112  * more details.
113 
114  -----BEGIN PGP SIGNATURE-----
115  Version: GnuPG v1.4.9 (Darwin)
116 
117  iQIcBAEBAgAGBQJRSsfJAAoJEAMIAO35UbuOQT8P/RJbka8etf7wbxdHQNAY+2cC
118  vDf8J3X8VI+pwMqv6wgTVy17venMZJa4I4ikXD/MRyWV1XbTG0mBXk/7AZk7Rexk
119  KTvL/U1kWiez6+8XXLye+k2JNM6v7eej8xMrqEcO0ZArh/DsLoIn1y8p8qjBI7+m
120  aE7lhstDiD0z8mwRRLKFLN2IH5rAFaZZUvj5ERJaoYUKdn4c+RcQVei2YOl4T0FU
121  LWND3YLoH8naqJXkaOKEN4UfJINCwxhe5Ke9wyfLWLUO7NamRkWD2T7CJ0xocnD1
122  sjAzlVGNgaFDRflfIF4QhBx1Ddl6wwhJfw+d08bjqblSq8aXDkmFA7HeunSFKkdn
123  oIEOEgyj+veuOMRJC5pnBJ9vV+7qRdDKQWaCKotynt4sWJDGQ9kWGWm74SsNaduN
124  TPMyr9kNmGsfR69Q2Zq/FLcLX/j8ESxU+HYUB4vaARw2xEOu2xwDDv6jt0j3Vqsg
125  x7rWv4S/Eh18FDNDkVRChiNoOIilLYLL6c38uMf1pnItBuxP3uhgY6COm59kVaRh
126  nyGTYCDYD2TK+fI9o89F1297uDCwEJ62U0Q7iTDp5QuXCoxkPfv8/kX6lS6T3y9G
127  M9mqIoLbIQ1EDntFv7/t6fUTS2+46uCrdZWbQ5RjYXdrzjij02nDmJAm2BngnZvd
128  kamH0Y/n11lCvo1oQxM+
129  =uSzz
130  -----END PGP SIGNATURE-----
131  **************************************************************/
132 
133 #include "../core/stdafx.hpp"
134 
135 #include "OTClient.hpp"
136 #include "OTServerConnection.hpp"
137 #include "Helpers.hpp"
138 #include "OTWallet.hpp"
139 
140 #include "../ext/OTPayment.hpp"
141 
142 #include "../cash/Mint.hpp"
143 #include "../cash/Purse.hpp"
144 #include "../cash/Token.hpp"
145 
146 #include "../basket/Basket.hpp"
147 
148 #include "../core/recurring/OTPaymentPlan.hpp"
149 #include "../core/OTAccount.hpp"
150 #include "../core/OTAssetContract.hpp"
151 #include "../core/crypto/OTAsymmetricKey.hpp"
152 #include "../core/OTCheque.hpp"
153 #include "../core/crypto/OTEnvelope.hpp"
154 #include "../core/util/OTFolders.hpp"
155 #include "../core/OTLedger.hpp"
156 #include "../core/OTLog.hpp"
157 #include "../core/OTMessage.hpp"
158 #include "../core/crypto/OTNymOrSymmetricKey.hpp"
159 #include "../core/OTPayload.hpp"
160 #include "../core/OTPseudonym.hpp"
161 #include "../core/OTServerContract.hpp"
162 #include "../core/OTStorage.hpp"
163 #include "../core/trade/OTOffer.hpp"
164 #include "../core/trade/OTTrade.hpp"
165 #include "../core/util/StringUtils.hpp"
166 
167 #include <cstdio>
168 #include <memory>
169 
170 namespace opentxs
171 {
172 
173 int32_t OTClient::CalcReturnVal(const int64_t& lRequestNumber)
174 {
175  m_lMostRecentRequestNumber = lRequestNumber;
176  if ((-1) == lRequestNumber)
177  return (-1);
178  else if (0 == lRequestNumber)
179  return 0;
180 
181  const int32_t nRequestNum = static_cast<int32_t>(lRequestNumber);
182 
183  if (lRequestNumber == nRequestNum) // In this case, it works!
184  return nRequestNum;
185 
186  return (-2); // some other call can return m_lMostRecentRequestNumber
187  // if needed.
188 }
189 
190 void OTClient::ProcessMessageOut(const char* buf, int32_t* pnExpectReply) const
191 {
192  // otErr << "OTClient::ProcessMessageOut: \n\n" << buf << "\n\n";
193  //
194  // const OTString strMessage(buf);
195  // OTMessage tempMsg;
196  // tempMsg.LoadContractFromString(strMessage);
197 
198  m_pConnection->ProcessMessageOut(buf, pnExpectReply);
199 
200  // otErr << "OTClient::ProcessMessageOut: FINISHED.\n";
201 }
202 
203 void OTClient::ProcessMessageOut(const OTMessage& theMessage)
204 {
205  const OTString strMessage(theMessage);
206  // otErr << "OTClient::ProcessMessageOut: \n\n" << strMessage << "\n\n";
207  //
208 
209  // WHAT DOES THIS MEAN?
210 
211  // I means that later, if a message with a certain request number
212  // fails to reply, or show its face in the replies box, then I will
213  // have the option to look it up in the Outbuffer, based on that
214  // same request number, and send a re-try, or claw back any transaction
215  // numbers that might be on that message.
216 
217  // Should probably add an API call for specifically doing this, agnostic
218  // to whatever kind of transaction it actually is. Something like,
219  // OT_API_Message_HarvestClosingNumbers, and
220  // OT_API_Message_HarvestAllNumbers
221 
222  // So I can save the request number when sending a message, check for it
223  // later
224  // in the Nymbox, and then worst case, look it up in the Outbuffer and get
225  // my
226  // fucking transaction numbers back again!
227 
228  OTMessage* pMsg = new OTMessage; // a copy.
229  OT_ASSERT(nullptr != pMsg);
230 
231  if (pMsg->LoadContractFromString(strMessage))
232  m_MessageOutbuffer.AddSentMessage(*pMsg);
233  else {
234  // todo, log here.
235  delete pMsg;
236  pMsg = nullptr;
237  }
239  nullptr != m_pConnection,
240  "OTClient::ProcessMessageOut: ASSERT: nullptr != m_pConnection\n");
241 
242  m_pConnection->ProcessMessageOut(theMessage); // <===========
243 
244  // otErr << "OTClient::ProcessMessageOut: FINISHED.\n";
245 }
246 
247 bool OTClient::ProcessInBuffer(const OTMessage& theServerReply) const
248 {
250  nullptr != m_pConnection,
251  "OTClient::ProcessInBuffer: ASSERT: nullptr != m_pConnection\n");
252 
253  return m_pConnection->ProcessInBuffer(theServerReply);
254 }
255 
258 //
260  // OTServerConnection& theConnection,
261  const OTIdentifier& theServerID,
262  const OTServerContract& theServerContract,
263  OTPseudonym& theNym, OTMessage& theMessage)
264 {
265  if (theNymbox.GetTransactionCount() < 1) {
266  // If there aren't any notices in the nymbox, no point wasting a # to
267  // process an empty box.
268  otLog4 << __FUNCTION__ << ": Nymbox is empty.\n";
269 
270  return false;
271  }
272  else if (!theNymbox.VerifyAccount(theNym)) {
273  // If there aren't any notices in the nymbox, no point wasting a # to
274  // process an empty box.
275  otErr << __FUNCTION__ << ": Error: VerifyAccount() failed.\n";
276  return false;
277  }
278  OTPseudonym* pNym = &theNym;
279  // OTPseudonym * pNym = theConnection.GetNym();
280 
281  // OTIdentifier theServerID;
282  // theConnection.GetServerID(theServerID);
283  //
284  const OTIdentifier theNymID(*pNym);
285  const OTString strServerID(theServerID), strNymID(theNymID);
286 
287  int64_t lHighestNum = 0;
288  // get the last/current highest transaction number for the serverID.
289  // (making sure we're not being slipped any new ones with a lower value
290  // than this.)
291  const bool bGotHighestNum = pNym->GetHighestNum(strServerID, lHighestNum);
292 
293  // Contrasting Inbox and Nymbox.
294  //
295  // In "AcceptEntireInbox", I have to have a transaction number in order to
296  // accept the inbox.
297  // But I ALSO need to RECEIVE my transaction number THROUGH an inbox, so the
298  // server can get
299  // my signature on that number (that's the only way to hold me responsible
300  // for it, AND to
301  // later prove I'm NOT responsible for it when it's spent, without having to
302  // worry about
303  // saving account history forever, via so-called "destruction of account
304  // history.")
305  //
306  // So how can I receive a number, if I don't have anymore? My solution is
307  // to receive all
308  // transaction numbers through the NYMBOX, which is associated with Nym
309  // instead of asset account.
310  // That is: you RECEIVE numbers through the Nymbox, and you SPEND numbers
311  // through the Inbox.
312  //
313  // (You can also receive messages through your nymbox.) This way, I can
314  // require a transaction
315  // number for an INBOX (since asset accounts can have changing balances) but
316  // I do NOT have
317  // to require one for processing the NYMBOX (since users HAVE NO balances.)
318  // I can still
319  // get the signed receipt during this time in order to satisfy destruction
320  // of acct history.
321  // Perfect!
322  //
323  // Due to all this, lStoredTransactionNumber will be 0 for now. If I have
324  // to assign a number
325  // to it, then I will (probably the request number) but I will NOT be using
326  // a real
327  // transaction number here, since this is the NYMBOX.
328  //
329  int64_t lStoredTransactionNumber = 0;
330 
331  // the message to the server will contain a ledger to be processed for a
332  // specific acct. (in this case no acct, but user ID used twice instead.)
333  OTLedger processLedger(theNymbox.GetUserID(), theNymbox.GetUserID(),
334  theServerID);
335 
336  // bGenerateFile defaults to false on GenerateLedger call, so I left out the
337  // false.
338  processLedger.GenerateLedger(theNymbox.GetUserID(), theServerID,
339  OTLedger::message); // Can't just use one of
340  // these. It either has to
341  // be read out of a file or
342  // a string, or it has to be generated. So you construct it, then you either
343  // call GenerateLedger or LoadInbox, then you call VerifyContractID to make
344  // sure
345  // it loaded securely. (No need to verify if you just generated it.)
346 
348  theNymbox.GetUserID(), theNymbox.GetUserID(), theServerID,
349  OTTransaction::processNymbox, lStoredTransactionNumber);
350 
351  // This insures that the ledger will handle cleaning up the transaction, so
352  // I don't have to delete it later.
353  processLedger.AddTransaction(*pAcceptTransaction);
354 
355  // loop through the transactions in theNymbox, and create corresponding
356  // "accept" items
357  // for each one of the transfer requests. Each of those items will go into a
358  // single
359  // "process nymbox" transaction that I will add to the processledger and
360  // thus to the
361  // outgoing message.
362 
363  // theIssuedNym == transaction numbers being added.
364  // theRemovedNym == transaction numbers being removed. (finalReceipt
365  // notices about opening numbers for cron items.)
366  // theTentativeNym == transaction numbers being tentatively added. (I keep
367  // a )
368  //
369  OTPseudonym theIssuedNym, theRemovedNym;
370 
371  std::set<int64_t> setNoticeNumbers; // Trans#s I've successfully signed for,
372  // and have a notice of this from the
373  // server.
374 
375  // For each transaction in the nymbox, if it's in reference to a transaction
376  // request,
377  // then create an "accept" item for that blank transaction, and add it to my
378  // own, new,
379  // "process nymbox" transaction that I'm sending out.
380  //
381  for (auto& it : theNymbox.GetTransactionMap()) {
382  OTTransaction* pTransaction = it.second;
383  OT_ASSERT(nullptr != pTransaction);
384  // ------------------------------------------------------------
385  // This is now possible (abbreviated notices in the box), since we try
386  // to avoid
387  // downloading replyNotices if we can help it. So we only error if it's
388  // abbreviated
389  // but NOT a replyNotice.
390  //
391  if (pTransaction->IsAbbreviated() &&
392  (pTransaction->GetType() != OTTransaction::replyNotice)) {
393  otErr << __FUNCTION__ << ": Error: Unexpected abbreviated receipt "
394  "in Nymbox, even after supposedly loading "
395  "all box receipts. (And it's not a "
396  "replyNotice, either!)\n";
397  // return false;
398  }
399 
400  // OTString strTransaction(*pTransaction);
401  // otErr << "TRANSACTION CONTENTS:\n" << strTransaction << "\n";
402 
403  OTString strRespTo;
404  pTransaction->GetReferenceString(strRespTo);
405  // otErr << "TRANSACTION \"IN REFERENCE TO\"
406  // CONTENTS:\n" << strRespTo << "\n";
407  // MESSAGE (From Another Nym)
408  //
409  if ((OTTransaction::message == pTransaction->GetType())) {
411  *pAcceptTransaction, OTItem::acceptMessage);
412 
413  // The above already has OT_ASSERT so, no need to check the pointer
414  // for nullptr.
415 
416  // the transaction will handle cleaning up the transaction item.
417  pAcceptTransaction->AddItem(*pAcceptItem);
418 
419  pAcceptItem->SetReferenceToNum(
420  pTransaction->GetTransactionNum()); // This is critical. Server
421  // needs this to look up the
422  // receipt in my nymbox.
423  // Don't need to set transaction num on item since the constructor
424  // already got it off the owner transaction.
425 
426  // sign the item
427  pAcceptItem->SignContract(*pNym);
428  pAcceptItem->SaveContract();
429 
430  otInfo << __FUNCTION__
431  << ": Received an encrypted message in your Nymbox:\n"
432  << strRespTo << "\n";
433 
434  // Todo: really shouldn't do this until we get a successful REPLY
435  // from the server.
436  // That's when I do a lot of other things. But this is a no-biggie
437  // thing. It will almost
438  // always succeed and in the odd-event that it fails, I'll end up
439  // with a duplicate message
440  // in my mail. So what?
441  OTMessage* pMessage = new OTMessage;
442 
443  OT_ASSERT(nullptr != pMessage);
444 
445  // The original message that was sent to me (with an encrypted
446  // envelope in the payload,
447  // and with the sender's ID and recipient IDs as m_strNymID and
448  // m_strNymID2) is stored
449  // within strRespTo. Let's load it up into an OTMessage instance,
450  // and add it to pNym's mail.
451  //
452  if (pMessage->LoadContractFromString(strRespTo)) {
453  pNym->AddMail(*pMessage); // Now the Nym is responsible to
454  // delete it. It's in his "mail".
455  OTPseudonym* pSignerNym = pNym;
456  pNym->SaveSignedNymfile(*pSignerNym);
457  }
458  else {
459  delete pMessage; // Don't want to leak otherwise.
460  pMessage = nullptr;
461  }
462  } // if message
463 
464  // INSTRUMENT (From Another Nym)
465  //
466  if ((OTTransaction::instrumentNotice == pTransaction->GetType())) {
468  *pAcceptTransaction, OTItem::acceptNotice);
469 
470  // The above already has OT_ASSERT so, no need to check the pointer
471  // for nullptr.
472 
473  // the transaction will handle cleaning up the transaction item.
474  pAcceptTransaction->AddItem(*pAcceptItem);
475 
476  pAcceptItem->SetReferenceToNum(
477  pTransaction->GetTransactionNum()); // This is critical. Server
478  // needs this to look up the
479  // receipt in my nymbox.
480  // Don't need to set transaction num on item since the constructor
481  // already got it off the owner transaction.
482 
483  // sign the item
484  pAcceptItem->SignContract(*pNym);
485  pAcceptItem->SaveContract();
486 
487  otInfo << __FUNCTION__
488  << ": Received an encrypted instrument in your Nymbox:\n"
489  << strRespTo << "\n";
490  } // if instrument
491 
492  // SERVER NOTIFICATION
493  //
494  else if ((OTTransaction::notice == pTransaction->GetType())) {
496  *pAcceptTransaction, OTItem::acceptNotice);
497 
498  // The above already has OT_ASSERT so, no need to check the pointer
499  // for nullptr.
500 
501  // the transaction will handle cleaning up the transaction item.
502  pAcceptTransaction->AddItem(*pAcceptItem);
503 
504  pAcceptItem->SetReferenceToNum(
505  pTransaction->GetTransactionNum()); // This is critical. Server
506  // needs this to look up the
507  // receipt in my nymbox.
508  // FYI, we don't need to set transaction num on item, since the
509  // constructor already got it off the owner transaction.
510 
511  // sign the item
512  pAcceptItem->SignContract(*pNym);
513  pAcceptItem->SaveContract();
514 
515  // otOut << __FUNCTION__ << ": Received a server
516  // notification
517  // in your Nymbox:\n" << strRespTo << "\n";
518 
519  // Todo: stash these somewhere, just like messages are in the
520  // pNym->AddMail() feature.
521  // NOTE: Most likely we still stash these in the paymentInbox just
522  // the same as instrumentNotice (above)
523 
524  } // if notice
525 
526  // It's a NEW Transaction Number that I ALREADY signed for, and this
527  // notice means it was a success.
528  // The server puts these in the Nymbox just in case -- helps to prevent
529  // synchronization issues.
530  //
531  // This means the new number was successfully already added to me.
532  // Therefore I need to add it to my side also, so my balance agreements
533  // will work.
534  // However, ONLY if I find the number on my tentative list, where I
535  // stored when I
536  // first signed for the number, in order to make sure the server
537  // couldn't lie to me
538  // later by slipping me a successNotice for one I never really signed
539  // for.
540  //
541  else if ((OTTransaction::successNotice ==
542  pTransaction->GetType()) // if successNotice (new; ALREADY
543  // just added) transaction number.
544  ) {
545  // The numbers on this set were (1) received in a successNotice, (2)
546  // found on my Tentative list,
547  // and (3) Therefore have ALREADY been added as numbers in the past.
548  // Therefore I need to REMOVE
549  // them from my tentative list, and add them as actual transactions.
550  // I also need to update my
551  // "most recent" highest trans # to reflect these new numbers.
552  //
553  OTNumList theOutput;
554  pTransaction->GetNumList(theOutput); // Get the numlist from the
555  // successNotice transaction
556  std::set<int64_t> theNumbers; //
557  theOutput.Output(theNumbers); // Get the actual set of numbers from
558  // the numlist object.
559  // Iterate through those numbers...
560  //
561  for (auto& it : theNumbers) {
562  const int64_t lValue = it;
563 
564  if (!pNym->VerifyTentativeNum(strServerID, lValue))
565  otWarn << __FUNCTION__
566  << ": OTTransaction::successNotice: This wasn't on "
567  "my tentative list (" << lValue
568  << "), I must have already processed it. (Or there "
569  "was dropped message when I did, or the server "
570  "is trying to slip me an old number.\n)";
571  else
572  setNoticeNumbers.insert(lValue); // I only take the numbers
573  // that I had been
574  // expecting, as tentative
575  // numbers,
576  }
578  *pAcceptTransaction, OTItem::acceptNotice);
579 
580  // the transaction will handle cleaning up the transaction item.
581  pAcceptTransaction->AddItem(*pAcceptItem);
582 
583  pAcceptItem->SetReferenceToNum(
584  pTransaction->GetTransactionNum()); // This is critical. Server
585  // needs this to look up the
586  // original.
587  // Don't need to set transaction num on item since the constructor
588  // already got it off the owner transaction.
589 
590  // sign the item
591  pAcceptItem->SignContract(*pNym);
592  pAcceptItem->SaveContract();
593 
594  } // else if successNotice
595  else if ( // if replyNotice -- notice of a server reply I should have
596  // already received when I first sent the request.
597  // (Some server replies are important enough that they have
598  // a copy dropped into your Nymbox to make SURE you
599  // receive and process them.) I'll accept the notice (clear
600  // it from my nymbox) and also I'll process the
601  // original server reply message inside of it, in case due
602  // to some network issue, I've never seen it before.
603  //
604  (OTTransaction::replyNotice == pTransaction->GetType()))
605  // UPDATE: Clearly if I ALREADY processed the server reply, then I
606  // don't need to process it AGAIN, right?
607  // This replyNotice is only here JUST IN CASE. (In case I missed the
608  // reply originally.) Well, guess what?
609  // Now I have a list of request numbers stored on the Nym, that
610  // tells me definitively whether or not that
611  // Nym has seen the reply. (Clearly if the Nym has processed the
612  // reply already, he doesn't have to do it
613  // AGAIN, now does he? This notice was "just in case.")
614  //
615  // Therefore I will check to see if the request number for this
616  // replyNotice is in my list of "replies I've
617  // already seen." If it is, I can entirely skip this step, which
618  // would otherwise end up trying erroneously
619  // to process a server reply even though I had already processed it
620  // before.
621  {
622 
623  const bool bAlreadySeenIt = pNym->VerifyAcknowledgedNum(
624  strServerID, pTransaction->GetRequestNum()); // Client verifies
625  // it has already
626  // seen a server
627  // reply.
628 
629  if (bAlreadySeenIt) // if we've already seen the reply, then we're
630  // already signalling the server to remove this
631  continue; // replyNotice on its side anyway, since the
632  // notification is clearly accomplished.
633  //
634  else // But if we HAVEN'T already seen the server's reply, then
635  // lucky for us he dropped a copy into the Nymbox! Now we can
636  // process it!
637  {
639  *pAcceptTransaction, OTItem::acceptNotice);
640  OT_ASSERT_MSG(nullptr != pAcceptItem,
641  "OTItem * pAcceptItem = "
642  "OTItem::CreateItemFromTransaction(*"
643  "pAcceptTransaction, OTItem::acceptNotice); for "
644  "replyNotice.");
645 
646  // the transaction will handle cleaning up the transaction item.
647  pAcceptTransaction->AddItem(*pAcceptItem);
648 
649  pAcceptItem->SetReferenceToNum(
650  pTransaction->GetTransactionNum()); // This is critical.
651  // Server needs this to
652  // look up the original.
653  // Don't need to set transaction num on item since the
654  // constructor already got it off the owner transaction.
655 
656  // Load up the server's original reply message (from the
657  // server's transaction item, on the receipt from my Nymbox.)
658  // The whole reason that notice was placed in the Nymbox is so
659  // we would be guaranteed to receive and process it, in
660  // case the original reply was lost due to network problems.
661  // Some messages are too important to just "get lost."
662  // Therefore, even though we most likely ALREADY processed this
663  // server reply, we're still going to give it a shot
664  // to process right here and now, just as we're also telling the
665  // server to go ahead and clear it out of the Nymbox.
666  // The server's conscience is clear: he knows for SURE that I
667  // DID receive notice.
668 
669  OTItem* pItem = pTransaction->GetItem(OTItem::replyNotice);
670 
671  if ((nullptr != pItem) &&
672  OTItem::acknowledgement == pItem->GetStatus()) {
673  OTString strOriginalReply;
674  pItem->GetAttachment(strOriginalReply);
675 
676  if (!strOriginalReply.Exists()) {
677  otErr << __FUNCTION__ << ": Error loading original "
678  "server reply message from "
679  "replyNotice. (It appears to "
680  "be zero length.)\n";
681  }
682  else // strOriginalReply.Exists() == true.
683  {
684  OTMessage* pMessage = new OTMessage;
685  OT_ASSERT_MSG(pMessage != nullptr,
686  "OTClient::AcceptEntireNymbox: OTMessage "
687  "* pMessage = new OTMessage;");
688 
689  if (false ==
690  pMessage->LoadContractFromString(
691  strOriginalReply)) {
692  otErr << __FUNCTION__
693  << ": Failed loading original server reply "
694  "message from replyNotice:\n\n"
695  << strOriginalReply << "\n\n";
696  delete pMessage;
697  pMessage = nullptr;
698  }
699  else // Success loading the server's original reply up
700  // into an OTMessage, from a string.
701  {
702  //
703  // pMessage needs to be allocated on the heap since
704  // ProcessServerReply takes ownership of it.
705  // theNymbox is passed in as a pointer because it's
706  // an optional parameter, precisely meant for this
707  // situation, where theNymbox happens to be already
708  // loaded and we don't want it loading it again,
709  // with one copy ending up overwriting the other.
710  //
711  // const bool bProcessed =
713  *pMessage, &theNymbox); // ProcessServerReply
714  // sometimes has to load
715  // the Nymbox. Since we
716  // already have it
717  // loaded here, we pass
718  // it in so it won't get
719  // loaded twice.
720 
721  pMessage = nullptr; // We're done with it now.
722 
723  // By this point, I KNOW FOR A FACT that IF there
724  // was some network problem that caused a Nym to
725  // lose an important server message, that by now,
726  // the Nym HAS received and processed that server
727  // reply as appropriate, using the exact same
728  // function that would have been called, had the
729  // reply
730  // been properly received in the first place. It's
731  // as if it was never lost. (Vital for syncing.)
732  } // if success loading original reply message from
733  // server.
734  } // if strOriginalReply.Exists()
735  } // if the replyNotice item is not-nullptr and status is
736  // "success"
737  else { // nullptr or "rejected"
738  otOut << __FUNCTION__
739  << ": the replyNotice item was either nullptr, or "
740  "rejected. (Unexpectedly on either count.)\n";
741  }
742  //
743  // sign the item
744  pAcceptItem->SignContract(*pNym);
745  pAcceptItem->SaveContract();
746  } // If we haven't "already seen it" then we loaded it up (above)
747  // and processed the server reply.
748 
749  // Todo: notice that we remove the replyNotice from the Nymbox,
750  // whether we are actually able to successfully
751  // load the original message or not. But what if that fails? We have
752  // now just discarded the message. In the
753  // future, perhaps have a place where "failed messages go to die" so
754  // that vital data isn't lost in the event
755  // of some unanticipated future bug.
756 
757  } // else if replyNotice
758 
759  // It's a NEW Transaction Number (I need to sign for it.)
760  //
761  else if ((OTTransaction::blank ==
762  pTransaction->GetType()) // if blank (new; just added)
763  // transaction number.
764  ) {
765  // My new transaction agreement needs to reflect all these new
766  // transaction numbers
767  // that I'm signing for (or at least this one in this block) so I
768  // add them to this
769  // temp nym, and then harvest the ones onto it from theNym, and then
770  // send those
771  // numbers in the new transaction agreement. (Removing them
772  // immediately after, and
773  // then only adding them for real if we get a server
774  // acknowledgment.)
775  //
776  OTNumList theNumlist, theBlankList;
777  pTransaction->GetNumList(theNumlist);
778  std::set<int64_t> theNumbers;
779  theNumlist.Output(theNumbers);
780 
781  for (auto& it : theNumbers) {
782  const int64_t lTransactionNumber = it;
783  // Loop FOR EACH TRANSACTION NUMBER in the "blank" (there could
784  // be 20 of them...)
785  //
786  if (pNym->VerifyIssuedNum(
787  strServerID, lTransactionNumber)) // Trans number is
788  // already issued to
789  // this nym (must be
790  // an old notice.)
791  otOut << __FUNCTION__ << ": Attempted to accept a blank "
792  "transaction number that I "
793  "ALREADY HAD...(Skipping.)\n";
794  else if (pNym->VerifyTentativeNum(
795  strServerID, lTransactionNumber)) // Trans number
796  // is already on
797  // the tentative
798  // list (meaning
799  // it's already
800  // been
801  // accepted.)
802  otOut << __FUNCTION__
803  << ": Attempted to accept a blank transaction number "
804  "that I ALREADY ACCEPTED (it's on my tentative "
805  "list already; Skipping.)\n";
806  else if (bGotHighestNum &&
807  (lTransactionNumber <= lHighestNum)) // Man, this is
808  // old numbers
809  // we've already
810  // HAD before!
811  otOut << __FUNCTION__
812  << ": Attempted to accept a blank transaction number "
813  "that I've HAD BEFORE, or at least, is <= to ones "
814  "I've had before. (Skipping...)\n";
815  else {
816  theIssuedNym.AddIssuedNum(strServerID, lTransactionNumber);
817  theBlankList.Add(lTransactionNumber);
818  }
819  } // for-each
821  *pAcceptTransaction, OTItem::acceptTransaction);
822 
823  pAcceptItem->AddBlankNumbersToItem(theBlankList);
824 
825  // the transaction will handle cleaning up the transaction item.
826  pAcceptTransaction->AddItem(*pAcceptItem);
827 
828  pAcceptItem->SetReferenceToNum(
829  pTransaction->GetTransactionNum()); // This is critical. Server
830  // needs this to look up the
831  // original.
832  // Don't need to set transaction num on item since the constructor
833  // already got it off the owner transaction.
834 
835  // sign the item
836  pAcceptItem->SignContract(*pNym);
837  pAcceptItem->SaveContract();
838  } // else if blank
839 
840  // It's a Final Receipt (In the Nymbox, this means an opening
841  // transaction
842  // number has been removed from my issued list on the server side.)
843  //
844  else if ((OTTransaction::finalReceipt ==
845  pTransaction->GetType()) // if finalReceipt (just removed)
846  // transaction number.
847  ) {
848  // Todo security: make sure this is only possible for finalReceipts,
849  // in case of abuse.
850  // Not only for finalReceipts, but for specific finalReceipt #s that
851  // I store a local list of, perhaps
852  // in my Nym, to track until they are closed. No other number should
853  // get through here.
854  // Otherwise the server could trick you into removing your issued
855  // numbers, simply by dropping
856  // a final receipt for the appropriate number!
857  // The server already keeps a list on its side to protect it from
858  // this possibility, but now it
859  // appears that the client-side will have to do a similar thing.
860  // Sigh.
861 
862  // Since the "in reference to" (the original "opening" transaction#)
863  // is supposedly
864  // already closed, then let's just MAKE SURE of that, since
865  // otherwise it'll screw up
866  // my future balance agreements. (The instant a finalReceipt
867  // appears, the "in ref to" # is already gone..)
868  //
869  if (pNym->RemoveIssuedNum(*pNym, strServerID,
870  pTransaction->GetReferenceToNum(),
871  true)) // bool bSave=true
872  otWarn << __FUNCTION__
873  << ": **** Due to finding a finalReceipt, REMOVING "
874  "OPENING NUMBER FROM NYM: "
875  << pTransaction->GetReferenceToNum() << " \n";
876  else
877  otWarn << __FUNCTION__
878  << ": **** Noticed a finalReceipt, but Opening Number "
879  << pTransaction->GetReferenceToNum()
880  << " had ALREADY been removed from nym. \n";
881 
882  //
883  // pNym won't actually save unless it actually removes that #. If
884  // the #'s already NOT THERE,
885  // then the removal will fail, and thus it won't bother saving here.
886 
887  // The client side keeps a list of active (recurring) transactions.
888  // That is, smart contracts and payment plans. I don't think it
889  // keeps
890  // market offers in that list, since we already have a list of
891  // active
892  // market offers separately. And market offers produce final
893  // receipts,
894  // so basically this piece of code will be executed for all final
895  // receipts.
896  // It's not really necessary that it be called for market offers,
897  // but whatever.
898  // It is for the others.
899  //
900  // Notice even though the final receipt hasn't yet been cleared out
901  // of the box,
902  // we are already removing the record of the active cron receipt.
903  // Why?
904  // Because regardless of when the user processes the finalReceipt,
905  // we know for
906  // a fact the transaction is no longer actively running on Cron. So
907  // we don't want
908  // to keep it on our list of "active" cron items if we know it's
909  // already inactive.
910  //
912  pTransaction->GetReferenceToNum(), pNym->GetConstID(),
913  pTransaction->GetPurportedServerID());
915  *pAcceptTransaction, OTItem::acceptFinalReceipt);
916 
917  // the transaction will handle cleaning up the transaction item.
918  pAcceptTransaction->AddItem(*pAcceptItem);
919 
920  pAcceptItem->SetReferenceToNum(
921  pTransaction->GetTransactionNum()); // This is critical. Server
922  // needs this to look up the
923  // original.
924  // Don't need to set transaction num on item since the constructor
925  // already got it off the owner transaction.
926 
927  pAcceptItem->SignContract(*pNym);
928  pAcceptItem->SaveContract();
929  } // else if finalReceipt (in Nymbox, this signals that an OPENING
930  // number has closed ALREADY. Thus no need to have a "closing
931  // process.")
932  }
933 
934  // If the above processing resulted in us actually accepting certain
935  // specific items,
936  // then let's process the message out to the server.
937  //
938  if (pAcceptTransaction->GetItemCount()) {
939  // IF there were transactions that were approved for me, (and I have
940  // notice of them in my nymbox)
941  // then they will be in this set. Also, they'll only be here IF they
942  // were verified as ACTUALLY being
943  // on my tentative list.
944  // Therefore need to REMOVE from Tentative list, and add to actual
945  // issued/available lists.
946  //
947  if (!setNoticeNumbers.empty()) {
948  //
949  // Note: No need to update highest num here, since that should have
950  // already been done when they were
951  // added to my issued list in the first place. (Removed from
952  // tentative.)
953  //
954  // int64_t lViolator = pNym->UpdateHighestNum(*pNym,
955  // strServerID, setNoticeNumbers); // bSave=false (saved below if
956  // necessary)
957  //
958  // if (lViolator != 0)
959  // otErr << "OTClient::AcceptEntireNymbox:
960  // ERROR: Tried to update highest trans # for a server, with lower
961  // numbers!\n"
962  // "This should NEVER HAPPEN, since
963  // these numbers are supposedly verified already before even getting
964  // this far.\n"
965  // "Violating number (too low): " <<
966  // lViolator << ",
967  // Nym ID: " << strNymID << " \n";
968  // else
969  {
970  for (auto& it : setNoticeNumbers) {
971  const int64_t lNoticeNum = it;
972 
973  if (pNym->RemoveTentativeNum(
974  strServerID,
975  lNoticeNum)) // doesn't save (but saved below)
976  pNym->AddTransactionNum(
977  *pNym, strServerID, lNoticeNum,
978  false); // bSave = false (but saved below...)
979  }
980 
981  // The notice means it already happened in the past. I already
982  // accepted the transaction # in my past,
983  // and now there is a notice of that fact sitting in my Nymbox.
984  // Until I recognize it, all my transaction
985  // statements will fail. (Like the one a few lines below
986  // here...)
987  //
988  pNym->SaveSignedNymfile(*pNym);
989  }
990  }
991 
992  if (ProcessUserCommand(
993  OTClient::processNymbox, theMessage, *pNym,
994  // *(pAssetContract),
995  theServerContract,
996  // *(theConnection.GetServerContract()),
997  nullptr) > 0) {
998  // the message is all set up and ready to go out... it's even
999  // signed.
1000  // Except the ledger we're sending, still needs to be added, and
1001  // then the
1002  // message needs to be re-signed as a result of that.
1003 
1004  theNymbox.ReleaseTransactions(); // Since this function accepts them
1005  // ALL, the new balance agreement
1006  // needs to show it as empty.
1007 
1008  // By this point, theIssuedNym contains a list of all the
1009  // transaction numbers that are in my
1010  // nymbox, and that WILL be ADDED to me once this processNymbox is
1011  // processed.
1012  // Therefore I need to ADD those items to my issued list (at least
1013  // temporarily) in order to
1014  // calculate the transaction agreement properly. So I used
1015  // theIssueNym as a temp variable to store those
1016  // numbers, so I can add them to my Nym and them remove them again
1017  // after generating the statement.
1018  //
1019  for (int32_t i = 0; i < theIssuedNym.GetIssuedNumCount(theServerID);
1020  i++) {
1021  int64_t lTemp = theIssuedNym.GetIssuedNum(theServerID, i);
1022  // We know it's not already issued on the Nym, or it wouldn't
1023  // have even gotten
1024  // set inside theIssuedNym in the first place (further up
1025  // above.) That's why
1026  // we are confident now that we can add it, generate the
1027  // transaction statement,
1028  // and then remove it again.
1029  //
1030  pNym->AddIssuedNum(strServerID, lTemp); // doesn't save.
1031  }
1032 
1033  // TRANSACTION STATEMENT
1034  // The item is signed and saved within this call as well. No need to
1035  // do that again.
1036  //
1037  OTItem* pBalanceItem =
1038  pNym->GenerateTransactionStatement(*pAcceptTransaction);
1039 
1040  // Here I am removing the new numbers again, now that the statement
1041  // has been generated.
1042  // If the message is successful, then I will need to add them for
1043  // real.
1044  //
1045  bool bAddedTentative = false;
1046  for (int32_t i = 0; i < theIssuedNym.GetIssuedNumCount(theServerID);
1047  i++) {
1048  int64_t lTemp = theIssuedNym.GetIssuedNum(theServerID, i);
1049  pNym->RemoveIssuedNum(strServerID, lTemp);
1050  pNym->AddTentativeNum(strServerID,
1051  lTemp); // So when I see the success
1052  // notice later, I'll know the
1053  // server isn't lying. (Store a
1054  // copy here until then.)
1055  bAddedTentative = true;
1056  }
1057 
1058  if (bAddedTentative) pNym->SaveSignedNymfile(*pNym);
1059 
1060  if (nullptr !=
1061  pBalanceItem) // This can't be nullptr BTW, since there is
1062  // an OT_ASSERT in Generate call. But I
1063  // hate to use a pointer without checking
1064  // it.
1065  pAcceptTransaction->AddItem(*pBalanceItem); // Better not be
1066  // nullptr... message
1067  // will fail... But
1068  // better check
1069  // anyway.
1070  else
1071  otErr << __FUNCTION__ << ": This should never happen.\n";
1072 
1073  // Sign the accept transaction, as well as the message ledger
1074  // that we've just constructed containing it.
1075  pAcceptTransaction->SignContract(*pNym);
1076  pAcceptTransaction->SaveContract();
1077 
1078  processLedger.SignContract(*pNym);
1079  processLedger.SaveContract();
1080 
1081  // Extract the ledger into string form and add it as the payload on
1082  // the message.
1083  OTString strLedger(processLedger);
1084  theMessage.m_ascPayload.SetString(strLedger);
1085 
1086  // Release any other signatures from the message, since I know it
1087  // was signed already in the above call to ProcessUserCommand.
1088  theMessage.ReleaseSignatures();
1089 
1090  return true;
1091 
1092  // Sign it and send it out.
1093  // theConnection.SignAndSend(theMessage);
1094  // I could have called SignContract() and then
1095  // theConnection.ProcessMessageOut(message)
1096  // but I used the above function instead.
1097 
1098  }
1099  else
1100  otErr << __FUNCTION__
1101  << ": Error processing processNymbox command.\n";
1102  }
1103 
1104  return false;
1105 }
1106 
1107 // Done: Make sure that transaction numbers being TEMPORARILY REMOVED from Nym
1108 // (to generate a statement
1109 // or balance agreement) are not put BACK onto the Nym unless they REALLY WERE
1110 // THERE....
1111 //
1112 // What might be happening is this: We remove the finalReceipt to generate the
1113 // statement, but then ADD IT BACK
1114 // again to the Nym unless/until the request was successful. This is normal
1115 // behavior most of the time. BUT...
1116 // IN THE NYMBOX, the finalReceipt is ONLY A NOTICE, to aid removal, since it
1117 // was ALREADY REMOVED on the server's
1118 // side! There's no point adding it again -- it's GONE! WORSE: If due to
1119 // communication error, you never got
1120 // the server's reply -- well it's still on your list on the client side, isn't
1121 // it? But this time, you don't have
1122 // any convenient notice in your Nymbox informing you that it's supposed to be
1123 // gone. As far as you know, it's still
1124 // good. It's on your list, right? That's the whole problem. In Nymbox, it's a
1125 // NOTICE of a PAST EVENT. Just REMOVE
1126 // the issued number AS SOON AS YOU SEE THE NOTICE. This way your transaction
1127 // statement is sure to be accurate.
1128 //
1129 // This may not be the case for the Inbox (below) but the lesson is still
1130 // important: Some numbers are removed
1131 // IMMEDIATELY upon notice. Other numbers are removed only when the receipt has
1132 // been successfully closed, otherwise
1133 // they are added back on again. And furthermore, they are only added back on if
1134 // they really were found on your
1135 // Nym in the first place. OTHERWISE, a SECURITY WEAKNESS would occur, where the
1136 // server could TRICK you into adding
1137 // numbers to your list of responsibility, even though they hadn't been there in
1138 // the first place! You'd just do your
1139 // normal process of removing the number, sending the request, then adding the
1140 // number back again -- but if it hadn't
1141 // been there in the first place, it'd still be added back again! That's why
1142 // there needs to be a CLEAR DISTINCTION
1143 // in both functions (above and below) between NEW numbers being added to your
1144 // list, and numbers being removed
1145 // from your list, and between numbers that were already on your list, numbers
1146 // that were not already on your list,
1147 // and between numbers being removed from your list, and numbers that were
1148 // ALREADY REMOVED some time in the past.
1149 //
1150 // DONE: Verify all these places: AcceptEntireNymbox, AcceptEntireInbox, API
1151 // calls for accepting Inbox, OTClient
1152 // code for @processInbox and @processNymbox, and server code for those also.
1153 
1157 //
1159  const OTIdentifier& theServerID,
1160  const OTServerContract& theServerContract,
1161  OTPseudonym& theNym, const OTMessage&,
1162  const OTAccount& theAccount)
1163 {
1164  bool bSuccess = false;
1165  OTPseudonym* pNym = &theNym;
1166  OTIdentifier theAccountID(theInbox);
1167  const OTAccount* pAccount = &theAccount;
1168  // Need this for balance agreement (outbox hash)
1169  std::unique_ptr<OTLedger> pOutbox(pAccount->LoadOutbox(*pNym));
1170  if (nullptr == pOutbox) {
1171  otOut << "OTClient::AcceptEntireInbox: Failed loading outbox!\n";
1172  return false;
1173  }
1174 
1175  if (theInbox.GetTransactionCount() < 1) {
1176  // If there aren't any transactions in the inbox, no point wasting a #
1177  // to process an empty box.
1178  otOut << "OTClient::AcceptEntireInbox: no point wasting a transaction "
1179  "number in order to process an empty box\n";
1180 
1181  return false;
1182  }
1183  OTString strServerID(theServerID);
1184  int64_t lStoredTransactionNumber = 0;
1185  bool bGotTransNum = pNym->GetNextTransactionNum(
1186  *pNym, strServerID,
1187  lStoredTransactionNumber); // Warning: this saves the nym if successful.
1188  if (!bGotTransNum) {
1189  otOut << "Error: No transaction numbers are available. Suggest "
1190  "requesting the server for a new one.\n";
1191  return false;
1192  }
1193  // the message to the server will contain a ledger to be processed for a
1194  // specific acct.
1195  OTLedger processLedger(theInbox.GetUserID(), theAccountID, theServerID);
1196 
1197  // bGenerateFile defaults to false on GenerateLedger call, so I left out the
1198  // false.
1199  processLedger.GenerateLedger(theAccountID, theServerID,
1200  OTLedger::message); // Can't just use one of
1201  // these. It either has to
1202  // be read out of a file or
1203  // a string, or it has to be generated. So you construct it, then you either
1204  // call GenerateLedger or LoadInbox, then you call VerifyContractID to make
1205  // sure
1206  // it loaded securely. (No need to verify if you just generated it.)
1207 
1208  OTTransaction* pAcceptTransaction = OTTransaction::GenerateTransaction(
1209  theInbox.GetUserID(), theAccountID, theServerID,
1210  OTTransaction::processInbox, lStoredTransactionNumber);
1211 
1212  // This insures that the ledger will handle cleaning up the transaction, so
1213  // I don't have to delete it later.
1214  processLedger.AddTransaction(*pAcceptTransaction);
1215 
1216  // loop through the transactions in theInbox, and create corresponding
1217  // "accept" items
1218  // for each one of the transfer requests. Each of those items will go into a
1219  // single
1220  // "process inbox" transaction that I will add to the processledger and thus
1221  // to the
1222  // outgoing message.
1223 
1224  int64_t lAdjustment = 0; // If I accept any pending transactions, I must
1225  // take note of any adjustment when I sign the
1226  // balance agreement.
1227 
1228  // If transaction #s that have been ISSUED to pNym are being REMOVED by this
1229  // transaction,
1230  // then we use this temporary Nym (theIssuedNym) to store the ones that are
1231  // being removed,
1232  // so that we can remove them from the Nym, calculate the balance agreement,
1233  // and then add them
1234  // back on again. (They aren't removed for real until the server "success"
1235  // reply comes back.)
1236  //
1237  // WARNING: Take care not to add them BACK, unless they really were there in
1238  // the first place!
1239  // Otherwise you have just been tricked into adding numbers to pNym that
1240  // weren't even there,
1241  // versus what you thought you were doing (re-adding numbers to pNym that
1242  // HAD been there, and
1243  // had only been temporarily removed in order to perform a calculation.)
1244  //
1245  //
1246  OTPseudonym theIssuedNym;
1247 
1248  // For each transaction in the inbox, if it's in reference to a transfer
1249  // request,
1250  // then create an "accept" item for that transfer request, and add it to my
1251  // own, new,
1252  // "process inbox" transaction that I'm sending out.
1253  //
1254  for (auto& it : theInbox.GetTransactionMap()) {
1255  OTTransaction* pTransaction = it.second;
1256  OT_ASSERT(nullptr != pTransaction);
1257 
1258  // If this transaction references the item that I'm trying to accept...
1259  if (pTransaction->GetReferenceToNum() >
1260  0) // if pointer not null AND it refers to some other transaction
1261  {
1262  OTString strRespTo;
1263  pTransaction->GetReferenceString(strRespTo);
1264 
1265  // Sometimes strRespTo contains an OTPaymentPlan or an OTTrade. (Or
1266  // an OTSmartContract.)
1267  // The rest of the time, it contains an OTItem.
1268  //
1269  // The reason is because in most cases I have the original item
1270  // right there, so I attach it. But with payment plans and trades,
1271  // the original payment plan itself, or trade itself, is being
1272  // loaded
1273  // by Cron (as a cron receipt) for reference reasons, and thus it is
1274  // the most appropriate object to attach in that case, and also, the
1275  // OTItem is not available in that context, since we aren't even
1276  // processing
1277  // a message, but rather, we are in Cron, processing a trade or some
1278  // other sort of cron item.
1279 
1280  // PAYMENT RECEIPT, MARKET RECEIPT
1281  //
1282  if ((OTTransaction::paymentReceipt == pTransaction->GetType()) ||
1283  (OTTransaction::marketReceipt == pTransaction->GetType())) {
1285  *pAcceptTransaction, OTItem::acceptCronReceipt);
1286 
1287  // the transaction will handle cleaning up the transaction item.
1288  pAcceptTransaction->AddItem(*pAcceptItem);
1289 
1290  // Set up the "accept" transaction item to be sent to the
1291  // server, by referencing
1292  // the transaction number of the receipt. Normally, when
1293  // accepting a pending
1294  // transaction, I set the "in reference to" to the transaction
1295  // number of the
1296  // original transfer that I am accepting.
1297  //
1298  // But I cannot do this with a market receipt, or a payment plan
1299  // receipt,
1300  // since there may be MULTIPLE RECEIPTS REFERENCING THE SAME
1301  // NUMBER (they will
1302  // all reference the original payment plan / offer.) Thus, in
1303  // the case of receipts,
1304  // I accept by setting the "in reference to" to the RECEIPT's
1305  // transaction number,
1306  // since each receipt represents a distinct transaction anyway,
1307  // and I must
1308  // accept them individually, and that is the number that
1309  // identifies them uniquely.
1310 
1311  pAcceptItem->SetNumberOfOrigin(*pTransaction);
1312 
1313  pAcceptItem->SetReferenceToNum(
1314  pTransaction->GetTransactionNum()); // This is critical.
1315  // Server needs this to
1316  // look up the receipt
1317  // in my inbox.
1318  // Don't need to set transaction num on item since the
1319  // constructor already got it off the owner transaction.
1320 
1321  // Nothing here to remove via theIssuedNym. In this case, the
1322  // transaction came from the server.
1323  // Only my ORIGINAL request to enter the payment plan can be
1324  // removed, and that only happens when the
1325  // finalReceipt is issued, not when a single receipt is removed.
1326  // Therefore, I can only accept the
1327  // receipt from my inbox, but this moment here doesn't free up
1328  // any of my issued transaction numbers.
1329  //
1330  // if (pNym->VerifyIssuedNum(strServerID,
1331  // pTransaction->GetTransactionNum()))
1332  // theIssuedNym.AddIssuedNum(strServerID,
1333  // pTransaction->GetTransactionNum());
1334  // else {error}
1335 
1336  // I don't attach the original payment plan or trade here,
1337  // because I already reference it by transaction num of the
1338  // receipt,
1339  // and the server can look it up in my inbox from there.
1340 
1341  // This just makes it convenient later.
1342  pAcceptItem->SetAmount(
1343  pTransaction->GetReceiptAmount()); // The server will verify
1344  // this actually matches,
1345  // or reject the message.
1346 
1347  // sign the item
1348  pAcceptItem->SignContract(*pNym);
1349  pAcceptItem->SaveContract();
1350  } // if market receipt or payment receipt (cron receipt)
1351 
1352  // FINAL RECEIPT, BASKET RECEIPT
1353  //
1354  if ((OTTransaction::finalReceipt == pTransaction->GetType()) ||
1355  (OTTransaction::basketReceipt == pTransaction->GetType())) {
1356  // Since the "in reference to" is supposedly already closed,
1357  // then let's just
1358  // MAKE SURE of that, since otherwise it'll screw up my future
1359  // balance agreements.
1360  // (The instant a finalReceipt appears, the "in ref to" is
1361  // already gone. Let it go.)
1362  //
1363  // todo security: make sure not to actually remove this number
1364  // unless I double check
1365  // it against a client-side list of open transaction numbers for
1366  // cron items (expecting
1367  // final receipts.) Unless the number appears on that list,
1368  // don't remove it. And then,
1369  // remove it from the list as well. The server already has a
1370  // mechanism like this.
1371  //
1372  if (OTTransaction::finalReceipt == pTransaction->GetType()) {
1373  if (pNym->RemoveIssuedNum(*pNym, strServerID,
1374  pTransaction->GetReferenceToNum(),
1375  true)) // bool bSave=true
1376  otWarn << "**** Due to finding a finalReceipt, "
1377  "REMOVING OPENING NUMBER FROM NYM: "
1378  << pTransaction->GetReferenceToNum() << " \n";
1379  else
1380  otWarn << "**** Noticed a finalReceipt, but Opening "
1381  "Number " << pTransaction->GetReferenceToNum()
1382  << " had ALREADY been removed from nym. \n";
1383 
1384  // The client side keeps a list of active (recurring)
1385  // transactions.
1386  // That is, smart contracts and payment plans. I don't think
1387  // it keeps
1388  // market offers in that list, since we already have a list
1389  // of active
1390  // market offers separately. And market offers produce final
1391  // receipts,
1392  // so basically this piece of code will be executed for all
1393  // final receipts.
1394  // It's not really necessary that it be called for market
1395  // offers, but whatever.
1396  // It is for the others.
1397  //
1398  // Notice even though the final receipt hasn't yet been
1399  // cleared out of the box,
1400  // we are already removing the record of the active cron
1401  // receipt. Why?
1402  // Because regardless of when the user processes the
1403  // finalReceipt, we know for
1404  // a fact the transaction is no longer actively running on
1405  // Cron. So we don't want
1406  // to keep it on our list of "active" cron items if we know
1407  // it's already inactive.
1408  //
1410  pTransaction->GetReferenceToNum(), pNym->GetConstID(),
1411  pTransaction->GetPurportedServerID());
1412  }
1413  //
1414  // pNym won't actually save unless it actually removes that #.
1415  // If the #'s already NOT THERE,
1416  // then the removal will fail, and thus it won't bother saving
1417  // here.
1418 
1419  // If I accept the finalReceipt or basketReceipt, that will
1420  // remove its CLOSING NUM from my issued list.
1421  // (Its "in reference to" num is already closed by now.)
1422  //
1423  // For security, ONLY add the number to theIssuedNym (to
1424  // indicate it's being removed) if
1425  // it WAS actually already there on the Nym. Otherwise it'll get
1426  // "re-added" when it wasn't
1427  // there in the first place!
1428  //
1429  if (false ==
1430  pNym->VerifyIssuedNum(strServerID,
1431  pTransaction->GetClosingNum()))
1432  otErr << "OTClient::AcceptEntireInbox: final or basket "
1433  "receipt, trying to 'remove' an issued number ("
1434  << pTransaction->GetClosingNum()
1435  << ") that already wasn't on my issued list. (So "
1436  "what is this in my inbox, then? Maybe you need "
1437  "to download a fresh copy of it.)\n";
1438 
1439  else // Success.
1440  {
1441  theIssuedNym.AddIssuedNum(strServerID,
1442  pTransaction->GetClosingNum());
1443 
1445  *pAcceptTransaction,
1446  (OTTransaction::finalReceipt == pTransaction->GetType())
1449 
1450  // the transaction will handle cleaning up the transaction
1451  // item.
1452  pAcceptTransaction->AddItem(*pAcceptItem);
1453 
1454  pAcceptItem->SetNumberOfOrigin(*pTransaction);
1455 
1456  pAcceptItem->SetReferenceToNum(
1457  pTransaction->GetTransactionNum()); // This is critical.
1458  // Server needs this
1459  // to look up the
1460  // receipt in my
1461  // inbox.
1462  // Don't need to set transaction num on item since the
1463  // constructor already got it off the owner transaction.
1464 
1465  // This just makes it convenient later.
1466  pAcceptItem->SetAmount(pTransaction->GetReceiptAmount());
1467  // The server will verify this actually matches, or reject
1468  // the message.
1469 
1470  // sign the item
1471  pAcceptItem->SignContract(*pNym);
1472  pAcceptItem->SaveContract();
1473  }
1474 
1475  } // if finalReceipt or basketReceipt
1476 
1477  // PENDING (incoming transfer)
1478  //
1479  else if ((OTTransaction::pending == pTransaction->GetType())) {
1480  std::unique_ptr<OTItem> pOriginalItem(
1482  strRespTo, theServerID,
1483  pTransaction->GetReferenceToNum()));
1484 
1485  // This item was attached as the "in reference to" item. Perhaps
1486  // Bob sent it to me.
1487  // Since that item was initiated by him, HIS would be the
1488  // account ID on it, not mine.
1489  // So I DON'T want to create it with my account ID on it.
1490  if (pOriginalItem) {
1491  if ((OTItem::transfer == pOriginalItem->GetType()) &&
1492  (OTItem::request ==
1493  pOriginalItem->GetStatus())) // I'm accepting a
1494  // transfer that was sent
1495  // to me. (A .PENDING
1496  // .TRANSFER .REQUEST)
1497  {
1499  *pAcceptTransaction, OTItem::acceptPending);
1500  // the transaction will handle cleaning up the
1501  // transaction item.
1502  pAcceptTransaction->AddItem(*pAcceptItem);
1503 
1504  // Set up the "accept" transaction item to be sent to
1505  // the server
1506  // (this item references and accepts another item by its
1507  // transaction number--
1508  // But on the server side, it doesn't look for the inbox
1509  // item with that number.
1510  // Rather, it looks for the inbox item that is "in
1511  // reference to" that number. Notice
1512  // therefore, my own accept item is below ALSO set to be
1513  // "in reference to" the number
1514  // of the original item. The server uses this info to
1515  // find the pending transaction.
1516 
1517  OTString strNote;
1518  pOriginalItem->GetNote(strNote);
1519 
1520  if (strNote.Exists()) pAcceptItem->SetNote(strNote);
1521  pAcceptItem->SetNumberOfOrigin(*pOriginalItem);
1522  pAcceptItem->SetReferenceToNum(
1523  pOriginalItem->GetTransactionNum()); // This is
1524  // critical.
1525  // Server needs
1526  // this to look
1527  // up the
1528  // original.
1529  // Don't need to set transaction num on item since the
1530  // constructor already got it off the owner transaction.
1531 
1532  // This just makes it convenient later.
1533  pAcceptItem->SetAmount(
1534  pTransaction->GetReceiptAmount()); // The server
1535  // will verify
1536  // this actually
1537  // matches, or
1538  // reject the
1539  // message.
1540 
1541  lAdjustment +=
1542  (pOriginalItem->GetAmount()); // Bob transferred me
1543  // 50 clams. If my
1544  // account was 100, it
1545  // WILL be 150.
1546  // Therefore,
1547  // adjustment is +50.
1548 
1549  // Nothing to remove in this case, since the transfer
1550  // was initiated by someone else.
1551  // if
1552  // (pNym->VerifyIssuedNum(strServerID,
1553  // pTransaction->GetTransactionNum()))
1554  // theIssuedNym.AddIssuedNum(strServerID,
1555  // pTransaction->GetTransactionNum());
1556  // else { error }
1557 
1558  // I don't attach the original item here because I
1559  // already reference it by transaction num,
1560  // and because the server already has it and sent it to
1561  // me. SO I just need to give the server
1562  // enough info to look it up again.
1563 
1564  // sign the item
1565  pAcceptItem->SignContract(*pNym);
1566  pAcceptItem->SaveContract();
1567  }
1568  else {
1569  const int32_t nOriginalType = pOriginalItem->GetType();
1570  otErr << "Unrecognized item type (" << nOriginalType
1571  << ") while processing inbox.\n(Only pending "
1572  "transfers, payment receipts, market "
1573  "receipts, cheque receipts, and transfer "
1574  "receipts are operational inbox items at this "
1575  "time.)\n";
1576  }
1577  }
1578  else {
1579  otErr << "Error loading transaction item from string in "
1580  "OTClient::AcceptEntireInbox\n";
1581  }
1582  } // else if pending
1583 
1584  // TRANSFER RECEIPT, CHEQUE RECEIPT
1585  //
1586  else if ((OTTransaction::transferReceipt ==
1587  pTransaction->GetType()) ||
1589  pTransaction->GetType()) ||
1591  pTransaction->GetType())) {
1592  std::unique_ptr<OTItem> pOriginalItem(
1594  strRespTo, theServerID,
1595  pTransaction->GetReferenceToNum()));
1596 
1597  // This item was attached as the "in reference to" item. Perhaps
1598  // Bob sent it to me.
1599  // Since that item was initiated by him, HIS would be the
1600  // account ID on it, not mine.
1601  // So I DON'T want to create it with my account ID on it.
1602  if (pOriginalItem) {
1603  if ((OTItem::request == pOriginalItem->GetStatus()) &&
1604  // In a real client, the user would pick and choose
1605  // which items he wanted
1606  // to accept or reject. We, on the other hand, are
1607  // blindly accepting all of
1608  // these types:
1609  (((OTItem::acceptPending ==
1610  pOriginalItem->GetType()) && // I'm accepting a
1611  // transfer receipt.
1613  pTransaction->GetType())) ||
1615  pOriginalItem->GetType()) && // I'm accepting a
1616  // notice that someone
1617  // cashed my cheque or
1618  // voucher.
1620  pTransaction->GetType()) ||
1622  pTransaction->GetType()))))) {
1623  // If pOriginalItem is acceptPending, that means I'm
1624  // accepting the transfer receipt from the server,
1625  // which has the recipient's acceptance inside of it as
1626  // the original item. This means the transfer that
1627  // *I* originally sent is now finally closed!
1628  //
1629  // If it's a depositCheque, that means I'm accepting the
1630  // cheque receipt from the server,
1631  // which has the recipient's deposit inside of it as the
1632  // original item. This means that the cheque that
1633  // *I* originally wrote is now finally closed!
1634  //
1635  // In both cases, the "original item" itself is not from
1636  // me, but from the recipient! Therefore,
1637  // the number on that item is useless to me (for
1638  // removing numbers from my own list of issued numbers.)
1639  // Rather, I need to load that original cheque, or
1640  // pending transfer, from WITHIN the original item,
1641  // in order to get THAT number, to remove it from my
1642  // issued list. *sigh*
1643  //
1644  if (OTItem::depositCheque ==
1645  pOriginalItem->GetType()) // We're accepting a
1646  // chequeReceipt or
1647  // voucherReceipt.
1648  {
1649  // Get the cheque from the Item and load it up into
1650  // a Cheque object.
1651  OTString strCheque;
1652  pOriginalItem->GetAttachment(strCheque);
1653 
1654  OTCheque theCheque; // allocated on the stack :-)
1655 
1656  if (false ==
1657  ((strCheque.GetLength() > 2) &&
1658  theCheque.LoadContractFromString(strCheque))) {
1659  otErr << "OTClient::" << __FUNCTION__
1660  << ": ERROR loading cheque or voucher "
1661  "from string:\n" << strCheque << "\n";
1662  }
1663  else {
1664  // IF it's actually there on pNym, then schedule
1665  // it for removal.
1666  // (Otherwise we'd end up improperly re-adding
1667  // it.)
1668  //
1669  if (false ==
1670  pNym->VerifyIssuedNum(
1671  strServerID,
1672  theCheque.GetTransactionNum()))
1673  otErr << "OTClient::" << __FUNCTION__
1674  << ": cheque or voucher receipt, "
1675  "trying to 'remove' an issued "
1676  "number ("
1677  << theCheque.GetTransactionNum()
1678  << ") that already wasn't on my "
1679  "issued list. (So what is this in "
1680  "my inbox, then? Maybe need to "
1681  "download a fresh copy of it.)\n";
1682  else {
1683  theIssuedNym.AddIssuedNum(
1684  strServerID,
1685  theCheque.GetTransactionNum());
1686 
1687  OTItem* pAcceptItem =
1689  *pAcceptTransaction,
1691  // the transaction will handle cleaning up
1692  // the transaction item.
1693  pAcceptTransaction->AddItem(*pAcceptItem);
1694 
1695  pAcceptItem->SetNumberOfOrigin(
1696  theCheque.GetTransactionNum());
1697 
1698  // In this case, this reference number is
1699  // someone else's responsibility, not mine.
1700  // (Someone ELSE deposited my cheque.)
1701  // ...But I still reference it.
1702  pAcceptItem->SetReferenceToNum(
1703  pOriginalItem
1704  ->GetTransactionNum()); // This is
1705  // critical.
1706  // Server
1707  // needs
1708  // this to
1709  // look up
1710  // the
1711  // original.
1712  // Don't need to set transaction num on item
1713  // since the constructor already got it off
1714  // the owner transaction.
1715 
1716  // Server rejects the message if these don't
1717  // match.
1718  pAcceptItem->SetAmount(
1719  pTransaction->GetReceiptAmount());
1720 
1721  // I don't attach the original item here
1722  // because I already reference it by
1723  // transaction num,
1724  // and because the server already has it and
1725  // sent it to me. SO I just need to give the
1726  // server
1727  // enough info to look it up again.
1728 
1729  // sign the item
1730  pAcceptItem->SignContract(*pNym);
1731  pAcceptItem->SaveContract();
1732  }
1733  }
1734  }
1735  else if (OTItem::acceptPending ==
1736  pOriginalItem->GetType()) // We're accepting
1737  // a
1738  // transferReceipt.
1739  {
1740  // IF it's actually there on pNym, then schedule it
1741  // for removal.
1742  // (Otherwise we'd end up improperly re-adding it.)
1743  //
1744  if (false ==
1745  pNym->VerifyIssuedNum(
1746  strServerID,
1747  pOriginalItem->GetNumberOfOrigin()))
1748  otErr << "OTClient::" << __FUNCTION__
1749  << ": transfer receipt, trying to "
1750  "'remove' an issued number ("
1751  << pOriginalItem->GetNumberOfOrigin()
1752  << ") that already wasn't on my issued "
1753  "list. (So what is this in my inbox, "
1754  "then? Maybe need to download a fresh "
1755  "copy of it.)\n";
1756  else {
1757  theIssuedNym.AddIssuedNum(
1758  strServerID,
1759  pOriginalItem->GetNumberOfOrigin());
1760 
1761  OTItem* pAcceptItem =
1763  *pAcceptTransaction,
1765  // the transaction will handle cleaning up the
1766  // transaction item.
1767  pAcceptTransaction->AddItem(*pAcceptItem);
1768 
1769  pAcceptItem->SetNumberOfOrigin(*pOriginalItem);
1770 
1771  // In this case, this reference number is
1772  // someone else's responsibility, not mine.
1773  // (Someone ELSE deposited my cheque.) ...But I
1774  // still reference it.
1775  pAcceptItem->SetReferenceToNum(
1776  pOriginalItem->GetTransactionNum()); // This
1777  // is
1778  // critical.
1779  // Server needs
1780  // this to look
1781  // up the
1782  // original.
1783  // Don't need to set transaction num on item
1784  // since the constructor already got it off the
1785  // owner transaction.
1786 
1787  pAcceptItem->SetAmount(
1788  pTransaction->GetReceiptAmount());
1789 
1790  // I don't attach the original item here because
1791  // I already reference it by transaction num,
1792  // and because the server already has it and
1793  // sent it to me. SO I just need to give the
1794  // server
1795  // enough info to look it up again.
1796 
1797  // sign the item
1798  pAcceptItem->SignContract(*pNym);
1799  pAcceptItem->SaveContract();
1800  }
1801  }
1802  else {
1803  otErr << "OTClient::AcceptEntireInbox: Error: "
1804  "wrong pOriginalItem type.\n";
1805  }
1806  }
1807  else {
1808  const int32_t nOriginalType = pOriginalItem->GetType();
1809  otErr << "Unrecognized item type (" << nOriginalType
1810  << ") while processing inbox.\n(Only pending "
1811  "transfers, payment receipts, market "
1812  "receipts, cheque receipts, and transfer "
1813  "receipts are operational inbox items at this "
1814  "time.)\n";
1815  }
1816  }
1817  else {
1818  otErr << "Error loading transaction item from string in "
1819  "OTClient::AcceptEntireInbox\n";
1820  }
1821  } // else if transfer receipt or cheque receipt. (item receipt)
1822 
1823  } // if pTransaction
1824 
1825  // This will have to go through the nymbox from now on, so I get
1826  // explicit sign-off on each number.
1827  // if (pTransaction)
1828  // {
1829  // HarvestTransactionNumbers(*pTransaction, *pNym);
1830  // }
1831  } // for --------------------------------------------
1832 
1833  // If the above processing resulted in us actually accepting certain
1834  // specific items,
1835  // then let's process the message out to the server.
1836  //
1837  if (pAcceptTransaction->GetItemCount()) {
1838  OTMessage theMessage;
1839  // OTAssetContract * pAssetContract =
1840  // theConnection.GetWallet()->GetAssetContract(pAccount->GetAssetTypeID());
1841 
1842  if (pAccount &&
1844  OTClient::processInbox, theMessage, *pNym,
1845  // *(pAssetContract),
1846  theServerContract,
1847  // *(theConnection.GetServerContract()),
1848  pAccount) > 0)) {
1849  // the message is all set up and ready to go out... it's even
1850  // signed.
1851  // Except the ledger we're sending, still needs to be added, and
1852  // then the
1853  // message needs to be re-signed as a result of that.
1854 
1855  theInbox.ReleaseTransactions(); // Since this function accepts them
1856  // ALL, the new balance agreement
1857  // needs to show it as empty.
1858 
1859  // By this point, theIssuedNym contains a list of all the
1860  // transaction numbers that are issued to me,
1861  // but that will NOT be issued to me anymore once this processInbox
1862  // is processed.
1863  // Therefore I need to REMOVE those items from my issued list (at
1864  // least temporarily) in order to
1865  // calculate the balance agreement properly. So I used theIssueNym
1866  // as a temp variable to store those
1867  // numbers, so I can remove them from my Nym and them add them again
1868  // after generating the statement.
1869  //
1870  for (int32_t i = 0; i < theIssuedNym.GetIssuedNumCount(theServerID);
1871  i++) {
1872  int64_t lTemp = theIssuedNym.GetIssuedNum(theServerID, i);
1873  pNym->RemoveIssuedNum(strServerID, lTemp);
1874  }
1875 
1876  // BALANCE AGREEMENT
1877  // The item is signed and saved within this call as well. No need to
1878  // do that again.
1879  OTItem* pBalanceItem = theInbox.GenerateBalanceStatement(
1880  lAdjustment, *pAcceptTransaction, *pNym, *pAccount, *pOutbox);
1881 
1882  // Here I am adding these numbers back again, since I removed them
1883  // to calculate the balance agreement.
1884  // (They won't be removed for real until I receive the server's
1885  // acknowledgment that those numbers
1886  // really were removed. Until then I have to keep them and use them
1887  // for my balance agreements.)
1888  for (int32_t i = 0; i < theIssuedNym.GetIssuedNumCount(theServerID);
1889  i++) {
1890  int64_t lTemp = theIssuedNym.GetIssuedNum(theServerID, i);
1891  pNym->AddIssuedNum(strServerID, lTemp);
1892  }
1893 
1894  if (nullptr != pBalanceItem)
1895  pAcceptTransaction->AddItem(*pBalanceItem); // Better not be
1896  // nullptr... message
1897  // will fail... But
1898  // better check
1899  // anyway.
1900  else
1901  otErr << "Should never happen.\n";
1902 
1903  // Sign the accept transaction, as well as the message ledger
1904  // that we've just constructed containing it.
1905  pAcceptTransaction->SignContract(*pNym);
1906  pAcceptTransaction->SaveContract();
1907 
1908  processLedger.SignContract(*pNym);
1909  processLedger.SaveContract();
1910 
1911  // Extract the ledger into string form and add it as the payload on
1912  // the message.
1913  OTString strLedger(processLedger);
1914  theMessage.m_ascPayload.SetString(strLedger);
1915 
1916  // Release any other signatures from the message, since I know it
1917  // was signed already in the above call to ProcessUserCommand.
1918  theMessage.ReleaseSignatures();
1919 
1920  // Sign it and send it out.
1921  // theConnection.SignAndSend(theMessage);
1922  // I could have called SignContract() and then
1923  // theConnection.ProcessMessageOut(message)
1924  // but I used the above function instead.
1925 
1926  bSuccess = true; // Otherwise we haven't really burned the
1927  // transaction num, and need to put it back
1928  // (below).
1929  }
1930  else
1931  otErr << "Error processing processInbox command in "
1932  "OTClient::AcceptEntireInbox\n";
1933  }
1934 
1935  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS.
1936  if (!bSuccess)
1937  pNym->AddTransactionNum(*pNym, strServerID, lStoredTransactionNumber,
1938  true); // bSave=true
1939 
1940  return bSuccess;
1941 }
1942 
1943 // I'm doing this so I can declare a local function, INSIDE this function :-)
1944 // (To avoid duplicating code.) Watch and learn...
1945 //
1947  const OTString& str_trans,
1948  OTString str_box_type,
1949  const int64_t& lTransNum,
1950  OTPseudonym& the_nym, OTLedger& ledger)
1951 {
1952  if (nullptr == ledger.GetTransaction(lTransNum)) // (Only add it if it's not
1953  // already there.)
1954  {
1955  OTTransactionType* pTransType =
1957 
1958  if (nullptr == pTransType)
1959  otErr << __FUNCTION__ << ": Error instantiating transaction type "
1960  "based on str_trans:\n" << str_trans
1961  << "\n";
1962  else {
1963  OTTransaction* pCopy = dynamic_cast<OTTransaction*>(pTransType);
1964 
1965  if (nullptr ==
1966  pCopy) // it's a transaction type but not a transaction.
1967  {
1968  const OTString strUserID(the_nym_id), strAcctID(the_nym_id);
1969  otOut
1970  << __FUNCTION__
1971  << ": it's a transaction type but not a transaction: (for "
1972  << str_box_type << "):\n\n" << str_trans << "\n\n";
1973  delete pTransType;
1974  pTransType = nullptr;
1975  }
1976  else // The copy transaction is now loaded from the string. Add it
1977  // to the ledger...
1978  {
1979  if (!ledger.AddTransaction(*pCopy)) // if unable to add that
1980  // transaction, once loaded,
1981  // signed, and saved, to the
1982  // paymentInbox or recordBox
1983  // ledger...
1984  {
1985  OTString strUserID(the_nym_id), strAcctID(the_nym_id);
1986  otOut << __FUNCTION__
1987  << ": Unable to add the transaction to the "
1988  << str_box_type
1989  << " with user/acct IDs: " << strUserID << " / "
1990  << strAcctID << ", and loading from string:\n\n"
1991  << str_trans << "\n\n";
1992  delete pCopy;
1993  pCopy = nullptr;
1994  }
1995  else // We were able to add it, so now let's save the
1996  // paymentInbox (or recordBox.)
1997  {
1998  ledger.ReleaseSignatures();
1999  ledger.SignContract(the_nym);
2000  ledger.SaveContract();
2001 
2002  if (OTLedger::paymentInbox == ledger.GetType())
2003  ledger.SavePaymentInbox();
2004  else if (OTLedger::recordBox == ledger.GetType())
2005  ledger.SaveRecordBox();
2006  else if (OTLedger::expiredBox == ledger.GetType())
2007  ledger.SaveExpiredBox();
2008 
2009  if (!pCopy->SaveBoxReceipt(ledger)) // <===================
2010  otErr
2011  << __FUNCTION__ << ": " << str_box_type
2012  << " Failed trying to SaveBoxReceipt. Contents:\n\n"
2013  << str_trans << "\n\n";
2014  }
2015  }
2016  } // else (pCopy not null.)
2017  } // if this transaction wasn't already in the paymentInbox / recordBox
2018  // (whichever was passed in)...
2019  // else it WAS already there, so do nothing. (No need to add it twice.)
2020 } // void load_str_trans_add_to_ledger
2021 
2057  OTMessage& theReply) const
2058 {
2059  const OTIdentifier ACCOUNT_ID(theReply.m_strAcctID);
2060  OTIdentifier SERVER_ID;
2061  theConnection.GetServerID(SERVER_ID);
2062  OTPseudonym* pNym = theConnection.GetNym();
2063  OTIdentifier USER_ID;
2064  pNym->GetIdentifier(USER_ID);
2065  const OTString strNymID(USER_ID);
2066  OTString strServerID(SERVER_ID),
2067  strReceiptID("ID_NOT_SET_YET"); // This will be user ID or acct ID
2068  // depending on whether trans statement
2069  // or balance statement.
2070 
2071  // todo fix cast.
2072  OTPseudonym* pServerNym = const_cast<OTPseudonym*>(
2073  theConnection.GetServerContract()->GetContractPublicNym());
2074  // The only incoming transactions that we actually care about are responses
2075  // to cash
2076  // WITHDRAWALS. (Cause we want to get that money off of the response, not
2077  // lose it.)
2078  // So let's just check to see if it's a withdrawal...
2079  OTLedger theLedger(USER_ID, ACCOUNT_ID, SERVER_ID);
2080  OTString strLedger(theReply.m_ascPayload);
2081 
2082  // The ledger we received from the server was generated there, so we don't
2083  // have to call GenerateLedger. We just load it.
2084  bool bSuccess =
2085  theLedger.LoadLedgerFromString(strLedger); // This is a MESSAGE ledger.
2086 
2087  if (bSuccess) bSuccess = theLedger.VerifyAccount((OTPseudonym&)*pServerNym);
2088 
2089  if (!bSuccess) {
2090  otErr << "ERROR loading ledger from message payload in "
2091  "OTClient::ProcessIncomingTransactions.\n";
2092  return;
2093  }
2094 
2095  otLog3 << "Loaded ledger out of message payload.\n";
2096 
2097  // Loop through ledger transactions,
2098 
2099  for (auto& it : theLedger.GetTransactionMap()) {
2100  OTTransaction* pTransaction = it.second;
2101  OT_ASSERT_MSG(nullptr != pTransaction, "nullptr transaction pointer in "
2102  "OTServer::"
2103  "UserCmdNotarizeTransactions\n");
2104 
2105  // See note above function. In this loop, it's possible that we've
2106  // already processed these
2107  // transactions. Therefore we ignore the ones that are already released
2108  // from our issued list.
2109  //
2110  if (false ==
2111  pNym->VerifyIssuedNum(strServerID,
2112  pTransaction->GetTransactionNum())) {
2113  otInfo << "OTClient::ProcessIncomingTransactions: Skipping "
2114  "processing of server reply to transaction number "
2115  << pTransaction->GetTransactionNum()
2116  << " since the number isn't even issued to me. Usually this "
2117  "means that I ALREADY processed it, and we are now "
2118  "processing the nymbox notice for the same "
2119  "transaction.\n";
2120  continue; // If this trans# isn't even signed out to me anymore,
2121  // then skip it. It's already closed.
2122  }
2123 
2124  // Each transaction in the ledger is a server reply to our original
2125  // transaction request.
2126  //
2127  if (pTransaction->VerifyAccount(*pServerNym)) // if not null && valid
2128  // transaction reply from
2129  // server
2130  {
2131  // We had to burn a transaction number to run the transaction that
2132  // the server has now replied to,
2133  // so let's remove that number from our list of responsibility.
2134  // Whether it was successful or not,
2135  // the server has removed it from our list of responsibility, so we
2136  // need to remove it on our side as well.
2137  // so that we can properly calculate our balance agreements in the
2138  // future.
2139  //
2140  // NOTE: not for all types! See the switch statements:
2141 
2142  OTItem::itemType theItemType = OTItem::error_state;
2143 
2144  switch (pTransaction->GetType()) {
2146  theItemType = OTItem::atDeposit;
2147  break;
2149  OTItem* pItemCash = pTransaction->GetItem(OTItem::atWithdrawal);
2150  OTItem* pItemVoucher =
2151  pTransaction->GetItem(OTItem::atWithdrawVoucher);
2152 
2153  if (nullptr != pItemCash)
2154  theItemType = OTItem::atWithdrawal;
2155  else if (nullptr != pItemVoucher)
2156  theItemType = OTItem::atWithdrawVoucher;
2157  } break;
2159  theItemType = OTItem::atPayDividend;
2160  break;
2162  theItemType = OTItem::atTransfer;
2163  break;
2165  theItemType = OTItem::atMarketOffer;
2166  break;
2168  theItemType = OTItem::atPaymentPlan;
2169  break;
2171  theItemType = OTItem::atSmartContract;
2172  break;
2174  theItemType = OTItem::atCancelCronItem;
2175  break;
2177  theItemType = OTItem::atExchangeBasket;
2178  break;
2179  default:
2180  case OTTransaction::atProcessInbox: // not handled here...
2181  continue;
2182  }
2183 
2184  switch (pTransaction->GetType()) {
2186  ProcessDepositResponse(*pTransaction, theConnection, theReply);
2187  pNym->RemoveIssuedNum(*pNym, strServerID,
2188  pTransaction->GetTransactionNum(),
2189  true); // bool bSave=true
2190  break;
2191 
2193  ProcessPayDividendResponse(*pTransaction, theConnection,
2194  theReply);
2195  pNym->RemoveIssuedNum(*pNym, strServerID,
2196  pTransaction->GetTransactionNum(),
2197  true); // bool bSave=true
2198  break;
2199 
2201  pNym->RemoveIssuedNum(*pNym, strServerID,
2202  pTransaction->GetTransactionNum(),
2203  true); // bool bSave=true
2204  // If the exchangeBasket FAILS, then I put all the transaction
2205  // numbers BACK on the Nym,
2206  // that had been taken for the exchange (for all the
2207  // basketReceipts.)
2208  {
2209  OTItem* pItem = pTransaction->GetItem(theItemType);
2210 
2211  if ((nullptr != pItem) &&
2212  OTItem::rejection == pItem->GetStatus()) // REJECTION
2213  {
2214  OTString strOriginalItem;
2215  pItem->GetReferenceString(strOriginalItem);
2216 
2217  OTTransactionType* pTempTransType =
2218  strOriginalItem.Exists()
2220  strOriginalItem)
2221  : nullptr;
2222 
2223  std::unique_ptr<OTItem> pOriginalItem(
2224  (nullptr == pTempTransType)
2225  ? nullptr
2226  : dynamic_cast<OTItem*>(pTempTransType));
2227 
2228  if (nullptr != pOriginalItem) {
2229  OTString strBasket;
2230  Basket theRequestBasket;
2231 
2232  pOriginalItem->GetAttachment(strBasket);
2233 
2234  if (strBasket.Exists() &&
2235  theRequestBasket.LoadContractFromString(
2236  strBasket))
2237  theRequestBasket.HarvestClosingNumbers(
2238  *pNym, SERVER_ID, true); // bSave=true
2239  else
2240  otErr << "(atExchangeBasket) Error loading "
2241  "original basket request in "
2242  "OTClient::"
2243  "ProcessIncomingTransactions\n";
2244  } // if success loading original item.
2245  else {
2246  otErr << "(atExchangeBasket) Error loading "
2247  "original item from string in "
2248  "OTClient::ProcessIncomingTransactions\n";
2249  }
2250  } // if exchangeBasket was a failure
2251  }
2252  break;
2253 
2255  pNym->RemoveIssuedNum(*pNym, strServerID,
2256  pTransaction->GetTransactionNum(),
2257  true); // bool bSave=true
2258  // Just above, we remove the issued number that was used to
2259  // initiate the cancelCronItem. (Regardless of success.)
2260  // Below, we remove the issued number that was ON that Cron Item
2261  // (IF SUCCESS.)
2262  {
2263  OTItem* pItem = pTransaction->GetItem(theItemType);
2264 
2265  if ((nullptr != pItem) &&
2267  pItem->GetStatus()) { // If it was a success
2268  // cancelling the cron item,
2269  // then the final receipt has
2270  // been created, and
2271  // the transaction number is closed out, and only the
2272  // closing number is left. If that is the case
2273  // then I can remove the transaction number from my
2274  // issued list, presumably the server already has.
2275  //
2276  OTString strOriginalItem;
2277  pItem->GetReferenceString(strOriginalItem);
2278 
2279  OTTransactionType* pTempTransType =
2280  strOriginalItem.Exists()
2282  strOriginalItem)
2283  : nullptr;
2284 
2285  std::unique_ptr<OTItem> pOriginalItem(
2286  (nullptr == pTempTransType)
2287  ? nullptr
2288  : dynamic_cast<OTItem*>(pTempTransType));
2289 
2290  if (nullptr != pOriginalItem) {
2291  if (false ==
2292  pNym->RemoveIssuedNum(
2293  *pNym, strServerID,
2294  pOriginalItem->GetReferenceToNum(),
2295  true)) // bool bSave=true
2296  {
2297  otErr << "(atCancelCronItem) Error removing "
2298  "issued number from user nym in "
2299  "OTClient::"
2300  "ProcessIncomingTransactions\n";
2301  }
2302  // I don't have to call RemoveTransactionNum for the
2303  // closing number (though the server does.) Why not?
2304  // Because I already called GetNextTransactionNum()
2305  // to use it in the first place, so it's already off
2306  // my
2307  // list of usable transaction numbers here on the
2308  // client side.
2309  }
2310  else {
2311  otErr << __FUNCTION__ << ": (atCancelCronItem) "
2312  "Error loading original "
2313  "item from string.\n";
2314  }
2315  }
2316  }
2317  break;
2318 
2320  ProcessWithdrawalResponse(*pTransaction, theConnection,
2321  theReply);
2322  pNym->RemoveIssuedNum(*pNym, strServerID,
2323  pTransaction->GetTransactionNum(),
2324  true); // bool bSave=true
2325  break;
2326 
2328  // Nothing removed here since the transaction number is still in
2329  // play, in this cases.
2330  // ACTUALLY, if this is a failure, we need to REMOVE from issued
2331  // list. (It's burned.)
2332  // But if success, the number stays in play until a later time.
2333  // (So we leave it issued.)
2334  {
2335  OTItem* pItem = pTransaction->GetItem(theItemType);
2336 
2337  if ((nullptr != pItem) &&
2338  OTItem::rejection == pItem->GetStatus()) {
2339  // Why do this? Oh I see, this number either gets burned
2340  // from the attempt,
2341  // or it stays open for a while if success. So here what
2342  // do we see? The rejection
2343  // burning the transaction number, but leaving it open
2344  // if success. Perfect.
2345  //
2346  if (false ==
2347  pNym->RemoveIssuedNum(
2348  *pNym, strServerID,
2349  pTransaction->GetTransactionNum(),
2350  true)) // bool bSave=true
2351  {
2352  otErr << __FUNCTION__ << ": Error removing issued "
2353  "number from user nym "
2354  "(for a transfer.)\n";
2355  }
2356  }
2357  }
2358  break;
2359 
2363 
2364  // Nothing removed here since the transaction number is still in
2365  // play, in these cases.
2366  // ACTUALLY, if these are a failure, we need to REMOVE from
2367  // issued list.
2368  // But if success, the number stays in play until a later time.
2369  {
2370  const int64_t lNymOpeningNumber =
2371  pTransaction->GetTransactionNum();
2372  OTItem* pReplyItem = pTransaction->GetItem(theItemType);
2373  if (nullptr != pReplyItem) {
2374  OTString strOriginalItem;
2375  pReplyItem->GetReferenceString(strOriginalItem);
2376 
2377  OTTransactionType* pTempTransType =
2378  strOriginalItem.Exists()
2380  strOriginalItem)
2381  : nullptr;
2382 
2383  std::unique_ptr<OTItem> pOriginalItem(
2384  (nullptr == pTempTransType)
2385  ? nullptr
2386  : dynamic_cast<OTItem*>(pTempTransType));
2387 
2388  if (nullptr != pOriginalItem) {
2389  OTString strCronItem;
2390  pOriginalItem->GetAttachment(strCronItem);
2391 
2392  // What kind of cron item is it?
2393  // Well (todo) we should probably double-check, but
2394  // the only cron items we
2395  // send notices for are payment plans and smart
2396  // contracts. Market offers don't
2397  // need notices, since anyone activating a market
2398  // offer is already getting the
2399  // reply. (AND getting a copy of that reply,
2400  // already, inside a replyNotice in
2401  // his Nymbox...) So he can't possibly miss the
2402  // server's reply, and there aren't
2403  // any other parties to notify (re: successful
2404  // activation), besides the Nym himself.
2405  //
2406  // Only payment plans and smart contracts could
2407  // potentially have some other signer, who
2408  // would want to get notified, and to whom the
2409  // notice is send.
2410  //
2411  std::unique_ptr<OTCronItem> pCronItem(
2412  strCronItem.Exists()
2413  ? OTCronItem::NewCronItem(strCronItem)
2414  : nullptr);
2415 
2416  if (nullptr != pCronItem) // the original smart
2417  // contract or payment plan
2418  // object.
2419  {
2420  if (OTItem::rejection ==
2421  pReplyItem->GetStatus()) // REJECTION (This
2422  // is where we
2423  // remove the
2424  // opening number,
2425  // and harvest the
2426  // closing
2427  // numbers.)
2428  {
2429  // Why do this? Oh I see, this number either
2430  // gets burned from the attempt,
2431  // or it stays open for a while if success.
2432  // So here what do we see? The rejection
2433  // burning the transaction number, but
2434  // leaving it open if success. Perfect.
2435  //
2436  if (false ==
2437  pNym->RemoveIssuedNum(
2438  *pNym, strServerID,
2439  lNymOpeningNumber,
2440  true)) // bool bSave=true
2441  {
2442  otErr << __FUNCTION__
2443  << ": Error removing issued "
2444  "number from user nym (for a "
2445  "cron item.)\n";
2446  }
2447  // If the activation was a failure, we can
2448  // add all the extra transaction numbers
2449  // BACK to the
2450  // Nym, that were being used as CLOSING
2451  // numbers, and use them later. (They aren't
2452  // burned.)
2453  // They're still all signed-out, so we
2454  // should harvest them so we can still use
2455  // them on something.
2456  // (Whereas if it had been a success, then
2457  // we would have left them in their existing
2458  // state, since
2459  // the transaction would then be in play,
2460  // and the numbers could not be used again,
2461  // nor removed as
2462  // issued numbers until the transaction
2463  // itself had finished and its receipts had
2464  // been signed-off.)
2465  //
2466  pCronItem->HarvestClosingNumbers(
2467  *pNym); // saves.
2468  }
2469  // When party receives notice that smart
2470  // contract has been activated,
2471  // remove the instrument from outpayments box.
2472  // (If it's there -- it can be.)
2473  //
2474  // (This happens for acknowledged AND rejected
2475  // smart contracts.)
2476  //
2477 
2478  OTString strInstrument; // If the instrument is
2479  // in the outpayments
2480  // box, we put a copy of
2481  // it here.
2482 
2484  pTransaction->GetType()) || // No need to
2485  // do this for
2486  // market
2487  // offers.
2488  // (Because
2489  // they don't
2491  pTransaction->GetType())) // go into the
2492  // outpayments
2493  // box in the
2494  // first place.)
2495  {
2496  // If success, save a copy in my "active
2497  // cron items" folder.
2498  //
2500  pReplyItem->GetStatus()) {
2501  pCronItem->SaveActiveCronReceipt(
2502  pNym->GetConstID());
2503  }
2504  OTNumList numlistOutpayment(
2505  lNymOpeningNumber);
2506  const int32_t nOutpaymentIndex =
2508  *pNym, lNymOpeningNumber);
2509  std::unique_ptr<OTMessage> theMessageAngel;
2510 
2511  if (nOutpaymentIndex >= 0) {
2512  OTMessage* pMsg =
2513  pNym->GetOutpaymentsByIndex(
2514  nOutpaymentIndex);
2515 
2516  if (nullptr == pMsg) {
2517  otErr << __FUNCTION__
2518  << ": Unable to find payment "
2519  "message in outpayment "
2520  "box based on index "
2521  << nOutpaymentIndex << ".\n";
2522  }
2523  else {
2524  const bool bRemovedOutpayment =
2526  nOutpaymentIndex,
2527  false); // bDeleteIt=false
2528  // (deleted later
2529  // on.)
2530  theMessageAngel.reset(pMsg);
2531  if (bRemovedOutpayment)
2532  pNym->SaveSignedNymfile(*pNym);
2533  else
2534  otErr << __FUNCTION__
2535  << ": Failed trying to "
2536  "remove outpayment at "
2537  "index: "
2538  << nOutpaymentIndex
2539  << "\n";
2540  if (!pMsg->m_ascPayload.GetString(
2541  strInstrument)) {
2542  otErr << __FUNCTION__
2543  << ": Unable to find "
2544  "payment instrument "
2545  "in outpayment "
2546  "message at index "
2547  << nOutpaymentIndex
2548  << ".\n";
2549  }
2550  else {
2551  // At this point, we've removed
2552  // the outpayment already, and
2553  // it will be deleted
2554  // when it goes out of scope
2555  // already. And we've got a copy
2556  // of the original financial
2557  // instrument that was SENT in
2558  // that outpayment.
2559  //
2560  // But what for? Why did I want
2561  // that instrument here in a
2562  // string, in strInstrument?
2563  // Do I still need to do
2564  // something with it? Yes: I
2565  // need to drop a copy of it
2566  // into
2567  // the record box!
2568 
2569  // NOTE: strInstrument is added
2570  // to the RecordBox below. So
2571  // there's no need to
2572  // do that here, ATM.
2573  }
2574  }
2575  } // if (nOutpaymentIndex >= 0)
2576  // When party receives notice that smart
2577  // contract has failed activation attempt,
2578  // then remove
2579  // the instrument from payments inbox AND
2580  // outpayments box. (If there -- could be
2581  // for either.)
2582  // (Outbox is done just above, so now let's
2583  // do inbox...)
2584  //
2585 
2586  // Why only rejected items? Why not remove
2587  // it from the payments inbox on success as
2588  // well?
2589  // Normally wouldn't we expect that a
2590  // successful activation of an inbox item,
2591  // should remove
2592  // that inbox item? Especially if there's
2593  // already a copy in the outbox as well...
2594  //
2595  // if
2596  // (OTItem::rejection ==
2597  // pReplyItem->GetStatus()) // REJECTION
2598  {
2599  const bool bExists1 = OTDB::Exists(
2600  OTFolders::PaymentInbox().Get(),
2601  strServerID.Get(), strNymID.Get());
2602  const bool bExists2 = OTDB::Exists(
2603  OTFolders::RecordBox().Get(),
2604  strServerID.Get(), strNymID.Get());
2605  OTLedger thePmntInbox(
2606  USER_ID, USER_ID,
2607  SERVER_ID); // payment inbox
2608  OTLedger theRecordBox(
2609  USER_ID, USER_ID,
2610  SERVER_ID); // record box
2611  bool bSuccessLoading1 =
2612  (bExists1 &&
2613  thePmntInbox.LoadPaymentInbox());
2614  bool bSuccessLoading2 =
2615  (bExists2 &&
2616  theRecordBox.LoadRecordBox());
2617  if (bExists1 && bSuccessLoading1)
2618  bSuccessLoading1 =
2619  (thePmntInbox
2620  .VerifyContractID() &&
2621  thePmntInbox.VerifySignature(
2622  *pNym));
2623  // bSuccessLoading1
2624  // =
2625  // (thePmntInbox.VerifyAccount(*pNym));
2626  // // (No need to load all the Box
2627  // Receipts using VerifyAccount)
2628  else if (!bExists1)
2629  bSuccessLoading1 =
2630  thePmntInbox.GenerateLedger(
2631  USER_ID, SERVER_ID,
2633  true); // bGenerateFile=true
2634  if (bExists2 && bSuccessLoading2)
2635  bSuccessLoading2 =
2636  (theRecordBox
2637  .VerifyContractID() &&
2638  theRecordBox.VerifySignature(
2639  *pNym));
2640  // bSuccessLoading2
2641  // =
2642  // (theRecordBox.VerifyAccount(*pNym));
2643  // // (No need to load all the Box
2644  // Receipts using VerifyAccount)
2645  else if (!bExists2)
2646  bSuccessLoading2 =
2647  theRecordBox.GenerateLedger(
2648  USER_ID, SERVER_ID,
2650  true); // bGenerateFile=true
2651  // by this point, the boxes DEFINITELY
2652  // exist -- or not. (generation might
2653  // have failed, or verification.)
2654  //
2655  if (!bSuccessLoading1 ||
2656  !bSuccessLoading2) {
2657  otOut
2658  << __FUNCTION__
2659  << ": while processing server "
2660  "reply containing rejection "
2661  "of cron item: WARNING: "
2662  "Unable to load, verify, or "
2663  "generate paymentInbox or "
2664  "recordBox, with IDs: "
2665  << strNymID << " / " << strNymID
2666  << "\n";
2667  }
2668  else // --- ELSE --- Success loading
2669  // the payment inbox and
2670  // recordBox and verifying
2671  { // their contractID and signature, (OR
2672  // success generating the ledger.)
2673  // See if there's a receipt in the
2674  // payments inbox.
2675  // If so, remove it.
2676  //
2677  // What's going on here?
2678  //
2679  // Well let's say Alice sends Bob a
2680  // payment plan. (This applies to
2681  // smart contracts, too.)
2682  // This means Bob has a payment plan
2683  // in his PAYMENTS INBOX, with the
2684  // recipient's (Alice)
2685  // transaction number set to X, and
2686  // the sender's transaction number
2687  // set to 0. It's 0 because
2688  // the instrument is still in Bob's
2689  // inbox -- he hasn't signed it yet
2690  // -- so his transaction
2691  // number isn't on it yet. It's
2692  // blank (0).
2693  //
2694  // Next, let's say Bob
2695  // signs/confirms the contract,
2696  // which puts a copy of it into his
2697  // PAYMENTS
2698  // OUTBOX. On the outbox version,
2699  // Alice's transaction number is X,
2700  // and Bob's transaction number
2701  // is Y.
2702  //
2703  // Later on, Bob needs to lookup the
2704  // payment plan in his PAYMENTS
2705  // INBOX (for example, to remove
2706  // it, AS YOU SEE IN THE BELOW
2707  // LOOP.) Remember, Bob's
2708  // transaction number is Y. But he
2709  // can't use
2710  // that number (Y) to lookup the
2711  // payment plan in his inbox, since
2712  // it's set to ZERO in his inbox!
2713  // The inbox version simply doesn't
2714  // HAVE Y set onto it yet -- only
2715  // the outbox version does.
2716  //
2717  // So how in the fuck does Bob
2718  // lookup the inbox version, if the
2719  // transaction number isn't SET on
2720  // it yet??
2721  //
2722  // The solution:
2723  // 1. Bob grabs an OTNumList
2724  // containing all the transaction
2725  // numbers from the OUTBOX VERSION,
2726  // which ends up containing "X,Y"
2727  // (that happens in this block.)
2728  // 2. Bob loops through the payments
2729  // INBOX, and for each, he grabs an
2730  // OTNumList containing all
2731  // the transaction numbers. One
2732  // of those (the matching one) will
2733  // contain "X,0". (Except it
2734  // will actually only contain
2735  // "X", since 0 is ignored in the
2736  // call to
2737  // GetAllTransactionNumbers.)
2738  // 3. Bob then checks like this:
2739  // if
2740  // (numlistOutpayment.VerifyAny(numlistIncomingPayment))
2741  // This is equivalent to saying:
2742  // if ("X,Y".VerifyAny("X")) which
2743  // RETURNS TRUE -- and we have
2744  // found the instrument!
2745 
2746  OTPayment theOutpayment;
2747 
2748  if (strInstrument.Exists() &&
2749  theOutpayment.SetPayment(
2750  strInstrument) &&
2751  theOutpayment.SetTempValues()) {
2752  theOutpayment
2754  numlistOutpayment);
2755  }
2756  const int32_t nTransCount =
2757  thePmntInbox
2758  .GetTransactionCount();
2759 
2760  for (int32_t ii = (nTransCount - 1);
2761  ii >= 0;
2762  --ii) // Count backwards since
2763  // we are removing
2764  // things.
2765  {
2766  std::unique_ptr<OTPayment>
2767  pPayment(GetInstrument(
2768  *pNym, ii,
2769  thePmntInbox));
2770 
2771  if (nullptr == pPayment) {
2772  otOut
2773  << __FUNCTION__
2774  << ": While looping "
2775  "payments inbox to "
2776  "remove a payment, "
2777  "unable to retrieve "
2778  "payment at index "
2779  << ii
2780  << " (skipping.)\n";
2781  continue;
2782  }
2783  else if (false ==
2784  pPayment
2785  ->SetTempValues()) {
2786  otOut
2787  << __FUNCTION__
2788  << ": While looping "
2789  "payments inbox to "
2790  "remove a payment, "
2791  "unable to set temp "
2792  "values for payment "
2793  "at index " << ii
2794  << " (skipping.)\n";
2795  continue;
2796  }
2797 
2798  OTNumList
2799  numlistIncomingPayment;
2800 
2801  pPayment
2802  ->GetAllTransactionNumbers(
2803  numlistIncomingPayment);
2804 
2805  if (numlistOutpayment.VerifyAny(
2806  numlistIncomingPayment)) {
2807  // ** It's the same
2808  // instrument.**
2809  // Remove it from the
2810  // payments inbox, and save.
2811  //
2812  OTTransaction*
2813  pTransPaymentInbox =
2814  thePmntInbox
2815  .GetTransactionByIndex(
2816  ii);
2817  OT_ASSERT(
2818  nullptr !=
2819  pTransPaymentInbox); // It DEFINITELY should be there. (Assert otherwise.)
2820  int64_t lPaymentTransNum =
2821  pTransPaymentInbox
2822  ->GetTransactionNum();
2823 
2824  // DON'T I NEED to call
2825  // DeleteBoxReceipt at this
2826  // point?
2827  // Since that needs to be
2828  // called now whenever
2829  // removing something from
2830  // any box?
2831  //
2832  // NOTE: might need to just
2833  // MOVE this box receipt to
2834  // the record box, instead
2835  // of
2836  // deleting it.
2837  //
2838  // Probably I need to do
2839  // that ONLY if the version
2840  // in the payments outbox
2841  // doesn't exist.
2842  // For example, if
2843  // strInstrument doesn't
2844  // exist, then there was
2845  // nothing in the payments
2846  // outbox, and therefore the
2847  // version in the payment
2848  // INBOX is the ONLY version
2849  // I have,
2850  // and therefore I should
2851  // stick it in the Record
2852  // Box.
2853  //
2854  // HOWEVER, if strInstrument
2855  // DOES exist, then I should
2856  // create its own
2857  // transaction to add
2858  // to the record box, and
2859  // delete the one that was
2860  // in the payment inbox. Why
2861  // delete it? Because
2862  // otherwise I would be
2863  // adding the same thing
2864  // TWICE to the record box,
2865  // which I don't really
2866  // need to do. And if I'm
2867  // going to choose one of
2868  // the two, the one in the
2869  // outpayments box will
2870  // be the more recent / more
2871  // relevant one of the two.
2872  // So I favor that one,
2873  // unless it doesn't
2874  // exist, in which case I
2875  // should add the other one
2876  // instead. (Todo.)
2877  //
2878  // NOTE: Until the above is
2879  // completed, the current
2880  // behavior is that the
2881  // outpayments box item
2882  // will be moved to the
2883  // record box if it exists,
2884  // and otherwise nothing
2885  // will be, since any
2886  // payments
2887  // inbox item will be
2888  // deleted.
2889 
2890  if (false ==
2891  thePmntInbox
2892  .DeleteBoxReceipt(
2893  lPaymentTransNum)) {
2894  otErr
2895  << __FUNCTION__
2896  << ": Failed "
2897  "trying to "
2898  "delete the box "
2899  "receipt for a "
2900  "transaction "
2901  "being removed "
2902  "from the "
2903  "payment "
2904  "inbox.\n";
2905  }
2906  if (thePmntInbox
2907  .RemoveTransaction(
2908  lPaymentTransNum)) {
2909  thePmntInbox
2910  .ReleaseSignatures();
2911  thePmntInbox
2912  .SignContract(
2913  *pNym);
2914  thePmntInbox
2915  .SaveContract();
2916 
2917  if (!thePmntInbox
2918  .SavePaymentInbox()) {
2919  otErr
2920  << __FUNCTION__
2921  << ": Failure "
2922  "while "
2923  "trying to "
2924  "save "
2925  "payment "
2926  "inbox.\n";
2927  }
2928  else {
2929  otOut
2930  << __FUNCTION__
2931  << ": Removed "
2932  "instrument "
2933  "from "
2934  "payment "
2935  "inbox."
2936  "\nSaved "
2937  "payment "
2938  "inbox.\n";
2939  }
2940  }
2941  else {
2942  otErr
2943  << __FUNCTION__
2944  << ": Failed "
2945  "trying to "
2946  "remove "
2947  "transaction "
2948  "from payment "
2949  "inbox. (Should "
2950  "never "
2951  "happen.)\n";
2952  }
2953  // Note: I could break right
2954  // here, if this is the only
2955  // transaction in the
2956  // payment inbox which
2957  // contains the instrument
2958  // in question. Which I
2959  // believe
2960  // it is. Todo: if that's
2961  // true, which I think it
2962  // is, then call break here.
2963  // After all, you wouldn't
2964  // send me the SAME
2965  // instrument TWICE, would
2966  // you?
2967  // But it still seems
2968  // theoretically possible
2969  // (albeit stupid.)
2970  }
2971  }
2972  // for (int32_t ii = 0; ii <
2973  // nTransCount; ++ii)
2974  // Also, if there was a message in
2975  // the outpayments box (which we
2976  // already removed
2977  // a bit above), go ahead and add a
2978  // receipt for it into the record
2979  // box.
2980  //
2981  if (strInstrument.Exists()) // Found
2982  // the
2983  // instrument in
2984  // the
2985  // outpayments
2986  // box.
2987  {
2988  OTTransaction* pNewTransaction =
2991  theRecordBox, // recordbox.
2992  OTTransaction::
2993  notice,
2994  lNymOpeningNumber);
2995  std::unique_ptr<OTTransaction>
2996  theTransactionAngel(
2997  pNewTransaction);
2998 
2999  if (nullptr !=
3000  pNewTransaction) // The
3001  // above
3002  // has an
3003  // OT_ASSERT
3004  // within,
3005  // but I
3006  // oust
3007  // like to
3008  // check my
3009  // pointers.
3010  {
3011  pNewTransaction
3013  lNymOpeningNumber); // Referencing myself here. We'll see how it works out.
3014  pNewTransaction
3016  strInstrument); // The
3017  // cheque,
3018  // invoice,
3019  // etc
3020  // that
3021  // used
3022  // to be
3023  // in
3024  // the
3025  // outpayments
3026  // box.
3027  pNewTransaction
3028  ->SignContract(*pNym);
3029  pNewTransaction
3030  ->SaveContract();
3031  const bool bAdded =
3032  theRecordBox
3033  .AddTransaction(
3034  *pNewTransaction);
3035 
3036  if (!bAdded) {
3037  otErr
3038  << __FUNCTION__
3039  << ": Unable to "
3040  "add "
3041  "transaction "
3042  << pNewTransaction
3043  ->GetTransactionNum()
3044  << " to record box "
3045  "(after "
3046  "tentatively "
3047  "removing from "
3048  "payment "
3049  "outbox, an "
3050  "action that is "
3051  "now "
3052  "canceled.)\n";
3053  }
3054  else {
3055  theTransactionAngel
3056  .release();
3057 
3058  theRecordBox
3059  .ReleaseSignatures();
3060  theRecordBox
3061  .SignContract(
3062  *pNym);
3063  theRecordBox
3064  .SaveContract();
3065  theRecordBox
3066  .SaveRecordBox(); // todo log failure.
3067 
3068  // Any
3069  // inbox/nymbox/outbox
3070  // ledger will only
3071  // itself contain
3072  // abbreviated versions
3073  // of the receipts,
3074  // including their
3075  // hashes.
3076  //
3077  // The rest is stored
3078  // separately, in the
3079  // box receipt, which is
3080  // created
3081  // whenever a receipt is
3082  // added to a box, and
3083  // deleted after a
3084  // receipt
3085  // is removed from a
3086  // box.
3087  //
3088  if (!pNewTransaction
3089  ->SaveBoxReceipt(
3090  theRecordBox)) // <===================
3091  {
3092  OTString strNewTransaction(
3093  *pNewTransaction);
3094  otErr
3095  << __FUNCTION__
3096  << ": for "
3097  "Record "
3098  "Box... "
3099  "Failed "
3100  "trying to "
3101  "SaveBoxRece"
3102  "ipt. "
3103  "Contents:"
3104  "\n\n"
3105  << strNewTransaction
3106  << "\n\n";
3107  }
3108  }
3109  } // if (nullptr !=
3110  // pNewTransaction)
3111  else // should never happen
3112  {
3113  otErr
3114  << __FUNCTION__
3115  << ": Failed while "
3116  "trying to generate "
3117  "transaction in "
3118  "order to add a new "
3119  "transaction to "
3120  "record box (for a "
3121  "payment instrument "
3122  "we just removed "
3123  "from the "
3124  "outpayments box): "
3125  << strNymID << "\n";
3126  }
3127  } // if (strInstrument.Exists())
3128  // (then add a copy to record
3129  // box.)
3130  } // else (Success loading the payment
3131  // inbox and recordBox)
3132  } // (OTItem::rejection ==
3133  // pReplyItem->GetStatus()) (loading
3134  // payment inbox and record box.)
3135  } // if payment plan or smart contract.
3136  } // if (nullptr != pCronItem)
3137  else {
3138  otErr << __FUNCTION__
3139  << ": Error loading cronitem from "
3140  "original item, from string:\n"
3141  << strOriginalItem << "\n";
3142  }
3143  } // if (nullptr != pOriginalItem)
3144  else {
3145  otErr << __FUNCTION__ << ": Error loading original "
3146  "item from string:\n"
3147  << strOriginalItem << "\n\n";
3148  }
3149  } // if (nullptr != pReplyItem)
3150  } // Case market offer, payment plan, or smart contract.
3151  break;
3152 
3153  default:
3154  // Error
3155  otErr << __FUNCTION__ << ": wrong transaction type: "
3156  << pTransaction->GetTypeString() << "\n";
3157  break;
3158  } // switch
3159  // -----------------------------------------------------------------
3160  // atTransfer: If success, KEEP the number on my list of
3161  // responsibility. If fail, REMOVE it.
3162  // (Do the same for atMarketOffer, atPaymentPlan,
3163  // and atSmartContract.)
3164  // atDeposit: Whether success or fail, remove the number from
3165  // my list of responsibility.
3166  // atWithdrawal: Whether success or fail, remove the number from
3167  // my list of responsibility.
3168  // atAcceptPending: Whether success or fail, remove the number
3169  // from my list of responsibility.
3170  //
3171  // SAVE THE RECEIPT....
3172  //
3173  // OTFolders::Receipt().Get()
3174  const OTString strServerID(SERVER_ID);
3175  OTString strReceiptFilename; // contains: strReceiptID .success,
3176  // fail, or error.
3177  OTItem* pItem = pTransaction->GetItem(OTItem::atBalanceStatement);
3178 
3179  if (nullptr == pItem) {
3180  pItem = pTransaction->GetItem(OTItem::atTransactionStatement);
3181 
3182  if (nullptr != pItem)
3183  pNym->GetIdentifier(strReceiptID); // In this case, the
3184  // receipt ID is the Nym
3185  // ID
3186  }
3187  else {
3188  strReceiptID = theReply.m_strAcctID; // If a balance statement,
3189  // then the receipt ID is
3190  // the Account ID.
3191  }
3192  // Try to save the transaction receipt to local storage.
3193  //
3194  OTString strTransaction;
3195  pTransaction->SaveContractRaw(strTransaction);
3196  OTString strFinal;
3197  OTASCIIArmor ascTemp(strTransaction);
3198 
3199  if (false ==
3200  ascTemp.WriteArmoredString(strFinal,
3201  "TRANSACTION")) // todo hardcoding.
3202  {
3203  otErr << __FUNCTION__ << ": Error saving transaction receipt "
3204  "(failed writing armored string):\n"
3206  << strServerID << OTLog::PathSeparator()
3207  << strReceiptFilename << "\n";
3208  return;
3209  }
3210  if (nullptr != pItem) {
3211  // Filename is based on transaction success/failure.
3212  //
3213  if (pTransaction->GetSuccess())
3214  strReceiptFilename.Format("%s.success", strReceiptID.Get());
3215  else
3216  strReceiptFilename.Format("%s.fail", strReceiptID.Get());
3217 
3219  strFinal.Get(), OTFolders::Receipt().Get(),
3220  strServerID.Get(), strReceiptFilename.Get());
3221  }
3222  else // This should never happen...
3223  {
3224  strReceiptFilename.Format("%s.error", strReceiptID.Get());
3225 
3226  otErr << __FUNCTION__
3227  << ": Error saving transaction receipt, since pItem was "
3228  "nullptr: " << strReceiptFilename << "\n";
3229 
3231  strFinal.Get(), OTFolders::Receipt().Get(),
3232  strServerID.Get(), strReceiptFilename.Get());
3233  }
3234 
3235  // No matter what kind of transaction it is,
3236  // let's see if the server gave us some new transaction numbers with
3237  // it...
3238  // UPDATE: the server will not give me transaction numbers unless I
3239  // have SIGNED FOR THEM.
3240  // Therefore, they are now dropped into the Nymbox, and that is
3241  // where they will be.
3242  // HarvestTransactionNumbers(*pTransaction, *pNym);
3243  }
3244  else {
3245  otOut
3246  << __FUNCTION__
3247  << ": Failed verifying server ownership of this transaction.\n";
3248  }
3249  }
3250 }
3251 
3253  OTTransaction& theTransaction, const OTServerConnection& theConnection,
3254  const OTMessage& theReply) const
3255 {
3256  const OTIdentifier ACCOUNT_ID(theReply.m_strAcctID);
3257  OTIdentifier SERVER_ID;
3258  theConnection.GetServerID(SERVER_ID);
3259  OTPseudonym* pNym = theConnection.GetNym();
3260  OTIdentifier USER_ID;
3261  pNym->GetIdentifier(USER_ID);
3262  // OTWallet * pWallet = theConnection.GetWallet();
3263 
3264  // loop through the ALL items that make up this transaction and check to see
3265  // if a response to pay dividend.
3266 
3267  for (auto& it : theTransaction.GetItemList()) {
3268  OTItem* pItem = it;
3269  OT_ASSERT(nullptr != pItem);
3270 
3271  // if pointer not null, and it's a dividend payout, and it's an
3272  // acknowledgement (not a rejection or error)
3273 
3274  if (OTItem::atPayDividend == pItem->GetType()) {
3275  if (OTItem::acknowledgement == pItem->GetStatus()) {
3276  otOut << "TRANSACTION SUCCESS -- Server acknowledges dividend "
3277  "payout.\n";
3278  }
3279  else {
3280  otOut << "TRANSACTION FAILURE -- Server rejects dividend "
3281  "payout.\n";
3282  }
3283  }
3284  }
3285 }
3286 
3288  const OTServerConnection& theConnection,
3289  const OTMessage& theReply) const
3290 {
3291  const OTIdentifier ACCOUNT_ID(theReply.m_strAcctID);
3292  OTIdentifier SERVER_ID;
3293  theConnection.GetServerID(SERVER_ID);
3294  OTPseudonym* pNym = theConnection.GetNym();
3295  OTIdentifier USER_ID;
3296  pNym->GetIdentifier(USER_ID);
3297  // OTWallet * pWallet = theConnection.GetWallet();
3298 
3299  // loop through the ALL items that make up this transaction and check to see
3300  // if a response to deposit.
3301 
3302  for (auto& it : theTransaction.GetItemList()) {
3303  OTItem* pReplyItem = it;
3304  OT_ASSERT(nullptr != pReplyItem);
3305 
3306  // if pointer not null, and it's a deposit, and it's an acknowledgement
3307  // (not a rejection or error)
3308 
3309  if ((OTItem::atDeposit == pReplyItem->GetType()) ||
3310  (OTItem::atDepositCheque == pReplyItem->GetType())) {
3311  if (OTItem::acknowledgement == pReplyItem->GetStatus()) {
3312  otOut
3313  << "TRANSACTION SUCCESS -- Server acknowledges deposit.\n";
3314 
3315  if (OTItem::atDepositCheque == pReplyItem->GetType()) {
3316  // Inside OT, when processing a successful server reply to a
3317  // depositCheque request,
3318  // and if that cheque is found inside the Payments Inbox,
3319  // ==> move it to the record box.
3320  //
3321  std::unique_ptr<OTLedger> pLedger(OTLedger::GenerateLedger(
3322  USER_ID, USER_ID, SERVER_ID, OTLedger::paymentInbox));
3323  // Beyond this point, I know that pLedger will need to be
3324  // deleted or returned.
3325  if ((nullptr != pLedger) && pLedger->LoadPaymentInbox() &&
3326  pLedger->VerifyAccount(*pNym)) {
3327  // If an incoming payment exists that matches the
3328  // instrument inside the server's deposit response,
3329  // then remove it from the payments inbox and save. Save
3330  // a copy to the records box.
3331  //
3332  // Response item contains a copy of the original item,
3333  // as reference string.
3334  //
3335  OTString strOriginalDepositItem;
3336  OTItem* pOriginalItem = nullptr;
3337  pReplyItem->GetReferenceString(strOriginalDepositItem);
3338 
3339  std::unique_ptr<OTTransactionType> pTransType(
3341  strOriginalDepositItem));
3342 
3343  if (nullptr != pTransType) {
3344  pOriginalItem =
3345  dynamic_cast<OTItem*>(pTransType.get());
3346  }
3347  if (nullptr != pOriginalItem) {
3348  OTString strCheque;
3349  pOriginalItem->GetAttachment(strCheque);
3350 
3351  OTCheque theCheque;
3352  bool bLoadContractFromString =
3353  theCheque.LoadContractFromString(strCheque);
3354 
3355  if (!bLoadContractFromString) {
3356  otErr << __FUNCTION__
3357  << ": ERROR loading cheque from string:\n"
3358  << strCheque << "\n";
3359  }
3360  else // Okay, we've got the cheque!
3361  {
3362  // Let's loop through the payment inbox and see
3363  // if there's a matching cheque.
3364  //
3365  const int64_t lChequeTransNum =
3366  theCheque.GetTransactionNum();
3367  const int32_t nTransCount =
3368  pLedger->GetTransactionCount();
3369 
3370  for (int32_t ii = (nTransCount - 1); ii >= 0;
3371  --ii) // going backwards since we are
3372  // deleting something. (Probably only
3373  // one thing, but still...)
3374  {
3375  std::unique_ptr<OTPayment> pPayment(
3376  GetInstrument(*pNym, ii, *pLedger));
3377 
3378  int64_t lPaymentTransNum = 0;
3379 
3380  if ((nullptr != pPayment) &&
3381  pPayment->SetTempValues() &&
3382  pPayment->GetTransactionNum(
3383  lPaymentTransNum) &&
3384  (lPaymentTransNum == lChequeTransNum)) {
3385  // It's the same cheque.
3386  // Remove it from the payments inbox,
3387  // and save.
3388  //
3389  OTTransaction* pTransaction =
3390  pLedger->GetTransactionByIndex(ii);
3391  OTString strPmntInboxTransaction;
3392  int64_t lRemoveTransaction = 0;
3393 
3394  if (nullptr != pTransaction) {
3395  pTransaction->SaveContractRaw(
3396  strPmntInboxTransaction);
3397  lRemoveTransaction =
3398  pTransaction
3399  ->GetTransactionNum();
3400 
3401  if (false ==
3402  pLedger->DeleteBoxReceipt(
3403  lRemoveTransaction)) {
3404  otErr << __FUNCTION__
3405  << ": Failed trying to "
3406  "delete the box "
3407  "receipt for a cheque "
3408  "being removed from a "
3409  "payments inbox: "
3410  << lRemoveTransaction
3411  << "\n";
3412  }
3413  if (pLedger->RemoveTransaction(
3414  lRemoveTransaction)) {
3415  pLedger->ReleaseSignatures();
3416  pLedger->SignContract(*pNym);
3417  pLedger->SaveContract();
3418 
3419  if (!pLedger
3420  ->SavePaymentInbox()) {
3421  otErr << __FUNCTION__
3422  << ": Failure while "
3423  "trying to save "
3424  "payment inbox.\n";
3425  }
3426  else {
3427  otOut
3428  << __FUNCTION__
3429  << ": Removed cheque "
3430  "from payments "
3431  "inbox. (Deposited "
3432  "successfully.)"
3433  "\nSaved payments "
3434  "inbox.\n";
3435  }
3436  }
3437  } // if (nullptr != pTransaction)
3438  // We're still in the loop backwards
3439  // through the paymentInbox, checking
3440  // each for
3441  // a payment instrument. Specifically,
3442  // theCheque's cheque. That's because
3443  // this is
3444  // processChequeResponse. If there was a
3445  // cheque in my payments inbox, and I
3446  // just
3447  // successfully deposited the cheque,
3448  // then I want to remove it from my
3449  // payments
3450  // inbox. We already just did that -- so
3451  // now we want to drop a copy of it into
3452  // the record box.
3453  //
3454  // Save a copy to the record box.
3455  //
3456  if (strPmntInboxTransaction.Exists()) {
3457  const OTString strNymID(USER_ID);
3458  const OTString strServerID(
3459  SERVER_ID);
3460  const bool bExists = OTDB::Exists(
3461  OTFolders::RecordBox().Get(),
3462  strServerID.Get(),
3463  strNymID.Get());
3464  OTLedger theRecordBox(
3465  USER_ID, USER_ID,
3466  SERVER_ID); // record box
3467  bool bSuccessLoading =
3468  (bExists &&
3469  theRecordBox.LoadRecordBox());
3470  if (bExists && bSuccessLoading)
3471  bSuccessLoading =
3472  (theRecordBox
3473  .VerifyContractID() &&
3474  theRecordBox
3475  .VerifySignature(
3476  *pNym));
3477  // bSuccessLoading
3478  // =
3479  // (theRecordBox.VerifyAccount(*pNym));
3480  // // (No need here to load all the
3481  // Box Receipts by using
3482  // VerifyAccount)
3483  else if (!bExists)
3484  bSuccessLoading =
3485  theRecordBox.GenerateLedger(
3486  USER_ID, SERVER_ID,
3488  true); // bGenerateFile=true
3489  // by this point, the nymbox
3490  // DEFINITELY exists -- or not.
3491  // (generation might have failed, or
3492  // verification.)
3493  //
3494  if (!bSuccessLoading) {
3495  OTString strUserID(USER_ID),
3496  strAcctID(USER_ID);
3497  otOut
3498  << __FUNCTION__
3499  << ": WARNING: Unable to "
3500  "load, verify, or "
3501  "generate recordBox, "
3502  "with IDs: " << strUserID
3503  << " / " << strAcctID
3504  << "\n";
3505  }
3506  else // --- ELSE --- Success
3507  // loading the recordBox and
3508  // verifying its contractID
3509  // and signature, (OR success
3510  // generating the ledger.)
3511  {
3512  // Currently in @getBoxReceipt,
3513  // we are taking an incoming
3514  // cheque from the nymbox
3515  // and adding it to the payments
3516  // inbox. From there the user
3517  // might choose to deposit it.
3518  // When he does that, he'll
3519  // receive a server reply, which
3520  // is what we're processing here
3521  // in this function. So now that
3522  // we've got that reply, we want
3523  // to move the cheque notice
3524  // from the payments inbox, and
3525  // into the record box at this
3526  // point HERE, when we've just
3527  // above removed it from the
3528  // payments inbox (on successful
3529  // deposit.)
3530  //
3532  USER_ID,
3533  strPmntInboxTransaction,
3534  "recordBox",
3535  lRemoveTransaction, *pNym,
3536  theRecordBox);
3537  }
3538  }
3539  } // pPayment
3540  }
3541  // for (payments inbox)
3542  }
3543  } // if nullptr != pOriginalItem
3544  }
3545  else {
3546  OTString strUserID(USER_ID), strAcctID(USER_ID);
3547  otWarn << __FUNCTION__ << ": Unable to load or verify "
3548  "payments inbox: User "
3549  << strUserID << " / Acct " << strAcctID << "\n";
3550  }
3551  }
3552  }
3553  else {
3554  otOut << __FUNCTION__
3555  << ": TRANSACTION FAILURE -- Server rejects deposit.\n";
3556  }
3557  }
3558  }
3559 }
3560 
3566  OTTransaction& theTransaction, const OTServerConnection& theConnection,
3567  const OTMessage& theReply) const
3568 {
3569  const OTIdentifier ACCOUNT_ID(theReply.m_strAcctID);
3570  OTIdentifier SERVER_ID;
3571  theConnection.GetServerID(SERVER_ID);
3572 
3573  OTString strServerID(SERVER_ID);
3574 
3575  OTPseudonym* pNym = theConnection.GetNym();
3576  OTIdentifier USER_ID;
3577  pNym->GetIdentifier(USER_ID);
3578 
3579  const OTString strUserID(USER_ID);
3580 
3581  OTWallet* pWallet = theConnection.GetWallet();
3582  OTPseudonym* pServerNym = const_cast<OTPseudonym*>(
3583  theConnection.GetServerContract()->GetContractPublicNym());
3584 
3585  // loop through the ALL items that make up this transaction and check to see
3586  // if a response to withdrawal.
3587 
3588  // if pointer not null, and it's a withdrawal, and it's an acknowledgement
3589  // (not a rejection or error)
3590  for (auto& it : theTransaction.GetItemList()) {
3591  OTItem* pItem = it;
3592  OT_ASSERT(nullptr != pItem);
3593  // VOUCHER WITHDRAWAL
3594  //
3595  // If we got a reply to a voucher withdrawal, we'll just display the
3596  // voucher
3597  // on the screen (if the server sent us one...)
3598  //
3599  if ((OTItem::atWithdrawVoucher == pItem->GetType()) &&
3600  (OTItem::acknowledgement == pItem->GetStatus())) {
3601  OTString strVoucher;
3602  OTCheque theVoucher;
3603 
3604  pItem->GetAttachment(strVoucher);
3605 
3606  if (theVoucher.LoadContractFromString(strVoucher)) {
3607  otOut << "\nReceived voucher from server:\n\n" << strVoucher
3608  << "\n\n";
3609  }
3610  }
3611  // CASH WITHDRAWAL
3612  //
3613  // If the item is a response to a cash withdrawal, we want to save the
3614  // coins into a purse
3615  // somewhere on the computer. That's cash! Gotta keep it safe.
3616  //
3617  else if ((OTItem::atWithdrawal == pItem->GetType()) &&
3618  (OTItem::acknowledgement == pItem->GetStatus())) {
3619  OTString strPurse;
3620  pItem->GetAttachment(strPurse);
3621 
3622  Purse thePurse(SERVER_ID);
3623 
3624  if (thePurse.LoadContractFromString(strPurse)) {
3625  // When we made the withdrawal request, we saved that purse
3626  // pointer in the
3627  // wallet so that we could get to the private coin unblinding
3628  // data when we
3629  // needed it (now).
3630  Purse* pRequestPurse = pWallet->GetPendingWithdrawal();
3631 
3632  OTString strAssetID(thePurse.GetAssetID());
3633  std::unique_ptr<Mint> pMint(
3634  Mint::MintFactory(strServerID, strAssetID));
3635  OT_ASSERT(nullptr != pMint);
3636  // Unlike the purse which we read out of a message,
3637  // now we try to open a purse as a file on the client side,
3638  // keyed by Asset ID. (The client should already have one
3639  // purse file for each asset type, if he already has cash.)
3640  //
3641  // We don't want to just overwrite that file. So instead, we
3642  // try to load that purse first, then add the token, then save
3643  // it
3644  // again.
3645  Purse theWalletPurse(thePurse);
3646  // TODO verify the wallet purse when loaded. My signature should
3647  // be the last thing on it.
3648 
3649  // TODO: I don't check this for failure. If the file doesn't
3650  // exist,
3651  // we are still going to save the purse there regardless.
3652  // HOWEVER need to make sure the wallet software has good backup
3653  // strategy. In the event that tokens are overwritten here, it
3654  // shouldn't be a problem since they would be in the archive
3655  // somewhere.
3656 
3657  theWalletPurse.LoadPurse(strServerID.Get(), strUserID.Get(),
3658  strAssetID.Get());
3659  // if Load, theWalletPurse.VerifySignature();
3660 
3661  bool bSuccess = false;
3662 
3663  if ((nullptr != pRequestPurse) && (nullptr != pServerNym) &&
3664  pMint->LoadMint() && pMint->VerifyMint(*pServerNym)) {
3665  Token* pToken = nullptr;
3666  while ((pToken = thePurse.Pop(*pNym)) != nullptr) {
3667  OT_ASSERT(nullptr != pToken);
3668 
3669  Token* pOriginalToken = pRequestPurse->Pop(*pNym);
3670 
3671  if (nullptr == pOriginalToken) {
3672  otErr << "ERROR, processing withdrawal response, "
3673  "but couldn't find original token:"
3674  << strPurse << "\n";
3675  }
3676  else if (Token::signedToken == pToken->GetState()) {
3677  otWarn << "Retrieved signed token from purse, and "
3678  "have corresponding withdrawal request "
3679  "in wallet. Unblinding...\n\n";
3680 
3681  if (pToken->ProcessToken(*pNym, *pMint,
3682  *pOriginalToken)) {
3683  // Now that it's processed, let's save it again.
3684  pToken->ReleaseSignatures();
3685  pToken->SignContract(*pNym);
3686  pToken->SaveContract();
3687 
3688  bSuccess = true;
3689 
3690  // add it to the existing client-side purse for
3691  // storing tokens of that asset type
3692  theWalletPurse.Push(*pNym, *pToken);
3693  }
3694  else {
3695  bSuccess = false;
3696  if (pToken) {
3697  delete pToken;
3698  pToken = nullptr;
3699  }
3700  // The while loop starts by allocating a
3701  // pOriginalToken, so I want to
3702  // delete it for each iteration and keep things
3703  // clean.
3704  if (pOriginalToken) {
3705  delete pOriginalToken;
3706  pOriginalToken = nullptr;
3707  }
3708  break;
3709  }
3710  }
3711 
3712  // The while loop starts by allocating a pToken, so I
3713  // want to
3714  // delete it for each iteration and keep things clean.
3715  if (nullptr != pToken) {
3716  delete pToken;
3717  pToken = nullptr;
3718  }
3719  // The while loop starts by allocating a pOriginalToken,
3720  // so I want to
3721  // delete it for each iteration and keep things clean.
3722  if (pOriginalToken) {
3723  delete pOriginalToken;
3724  pOriginalToken = nullptr;
3725  }
3726  } // while (pToken = thePurse.Pop(*pNym))
3727  }
3728 
3729  if (bSuccess) {
3730  // Sign it, save it.
3731  theWalletPurse.ReleaseSignatures(); // Might as well,
3732  // they're no good
3733  // anyway once the data
3734  // has changed.
3735  theWalletPurse.SignContract(*pNym);
3736  theWalletPurse.SaveContract();
3737  theWalletPurse.SavePurse(strServerID.Get(), strUserID.Get(),
3738  strAssetID.Get());
3739 
3740  otOut << "SUCCESSFULLY UNBLINDED token, and added the cash "
3741  "to the local purse, and saved.\n";
3742  }
3743  } // if (thePurse.LoadContractFromString(strPurse))
3744  }
3745  } // for
3746 }
3747 
3765  OTLedger* pNymbox) // IF the Nymbox
3766  // is passed in,
3767  // then use that
3768  // one, where
3769  // appropriate,
3770  // instead of
3771  // loading it
3772  // internally.
3773 {
3774  OT_ASSERT(nullptr != m_pConnection);
3775 
3776  OTServerConnection& theConnection = (*m_pConnection);
3777 
3778  OTIdentifier ACCOUNT_ID(theReply.m_strAcctID), SERVER_ID;
3779  theConnection.GetServerID(SERVER_ID);
3780 
3781  OTPseudonym* pNym = theConnection.GetNym();
3782  OTIdentifier USER_ID(*pNym);
3783  const OTString strServerID(SERVER_ID), strNymID(USER_ID);
3784  OTPseudonym* pServerNym = const_cast<OTPseudonym*>(
3785  theConnection.GetServerContract()->GetContractPublicNym());
3786 
3787  // Just like the server verifies all messages before processing them,
3788  // so does the client need to verify the signatures against each message
3789  // and verify the various contract IDs and signatures.
3790  if (!theReply.VerifySignature(*pServerNym)) {
3791  otErr << __FUNCTION__
3792  << ": Error: Server reply signature failed to verify.\n";
3793 
3794  OTMessage* pMessage =
3795  &theReply; // I'm responsible to cleanup this object.
3796  delete pMessage;
3797  pMessage = nullptr;
3798  return false;
3799  }
3801  atol(theReply.m_strRequestNum.Get()), strServerID,
3802  strNymID); // doesn't delete.
3803  // We couldn't find it in the "sent message" outbuffer (todo: persist this
3804  // buffer on the Nym.)
3805  // That means we must have missed the original server reply, even though it
3806  // DID happen. Then we
3807  // downloaded the Nymbox to re-sync after that failure occurred, and found
3808  // the reply there, and
3809  // processed it--removing it from the sent messages outbuffer at the same
3810  // time, since it was now
3811  // definitely handled.
3812  // FINALLY the network comes through with the server reply, and here we are
3813  // trying to process it
3814  // twice? But this time, it's NOT in the sent buffer, because we already
3815  // processed it -- so we
3816  // discard it! (todo: in a nice future version, save all of these in the
3817  // recordbox or something.)
3818  //
3819  // Here's another plausible scenario: You RECEIVE the server's reply
3820  // properly the first time, and
3821  // you process it. Of course, you STILL get the Nymbox copy of that same
3822  // message ("just in case") and
3823  // thus ProcessServerReply gets called a second time, again leading us to
3824  // this block of code right here...
3825  //
3826  if (nullptr == pSentMsg) //
3827  {
3828  const OTString strReply(theReply);
3829  otLog3 << __FUNCTION__
3830  << ": FYI: no record of server reply in sent messages buffer. "
3831  "We must have already processed it, and then removed it, "
3832  "earlier. (Discarding.) Reply message:\n\n" << strReply
3833  << "\n\n";
3834 
3835  OTMessage* pMessage =
3836  &theReply; // I'm responsible to cleanup this object.
3837  delete pMessage;
3838  pMessage = nullptr;
3839  return false;
3840  }
3841  // Below this point, we know we found the original sent message--still
3842  // cached as though its reply
3843  // hasn't been processed yet. We haven't processed it yet! We are now
3844  // supposedly, processing it for
3845  // the first and proper time! Therefore, let's remove it from the "sent
3846  // messages" outbuffer, so we
3847  // are able to tell, next time around, that this has already happened.
3848  // (After all, we don't want
3849  // the next FlushSentMessages call to claw back any transaction numbers when
3850  // we clearly had a proper
3851  // reply come through!)
3852  //
3853  const int64_t lReplyRequestNum = atol(theReply.m_strRequestNum.Get());
3854 
3855  // bool bRemoved =
3856  GetMessageOutbuffer().RemoveSentMessage(lReplyRequestNum, strServerID,
3857  strNymID); // deletes.
3858  bool bDirtyNym = false;
3859 
3860  // Similarly we keep a client side list of all the request numbers that we
3861  // KNOW we have
3862  // a server reply for. (Each ID is maintained until we see a mirror of it
3863  // appear in the server's
3864  // copy of that same list, and then we go ahead and remove it. This is
3865  // basically an optimization
3866  // trick that enables us to avoid downloading many box receipts -- the
3867  // replyNotices, specifically.)
3868  //
3869  if (pNym->AddAcknowledgedNum(strServerID,
3870  lReplyRequestNum)) // doesn't save (here).
3871  {
3872  bDirtyNym = true;
3873  }
3874 
3875  // Okay, we received a reply, so we added its request number to our list of
3876  // "replies we have definitely received."
3877  // But what about when the server sees that, and mirrors our list? It will
3878  // send its own list, containing that mirror.
3879  // Any number that appears there, can be removed from the local list
3880  // (confirmation is total by that point.)
3881  // Clearly the server KNOWS I saw his reply, since he copied my ack into his
3882  // ack mirror list. Therefore I have no
3883  // more reason to continue telling him that I got the reply -- he already
3884  // knows it! So I can remove the number from
3885  // my ack list, which will cause the server to do the same to match, once he
3886  // gets my next message.
3887  //
3888  // So next step: Loop through the ack list on the server reply, and any
3889  // numbers there can be REMOVED from the local
3890  // list...
3891  //
3892  std::set<int64_t> numlist_ack_reply;
3893  if (theReply.m_AcknowledgedReplies.Output(
3894  numlist_ack_reply)) // returns false if the numlist was empty.
3895  {
3896  for (auto& it : numlist_ack_reply) {
3897  const int64_t lTempRequestNum = it;
3898  OTPseudonym* pSignerNym = pNym;
3899 
3900  if (pNym->RemoveAcknowledgedNum(*pSignerNym, strServerID,
3901  lTempRequestNum,
3902  false)) // bSave=false
3903  bDirtyNym = true;
3904  }
3905  }
3906 
3907  if (bDirtyNym) {
3908  OTPseudonym* pSignerNym = pNym;
3909  pNym->SaveSignedNymfile(*pSignerNym);
3910  }
3911 
3912  // Done: Do a Get Sent Message based on request number. If we find the
3913  // sent message, then process the reply and Remove the sent message.
3914  // But if we do NOT find the sent message, then we must have processed it
3915  // already -- in which case discard it and return.
3916 
3917  // Here, the Client takes ownership of the message (so make sure it's
3918  // heap-allocated.)
3919  m_MessageBuffer.Push(theReply);
3920 
3921  // Once that process is done, everything below that line, in this function,
3922  // will be able to assume there is a verified Nym available, and a Server
3923  // Contract,
3924  // and an asset contract where applicable, and an account where applicable.
3925  //
3926  // Until that code is written, I do not have those things available to me.
3927  //
3928  // Furthermore also need to verify the payloads...
3929  // If "Command Responding To" was not actually signed by me, and I wasn't
3930  // expecting the new account request, then I do NOT want to sign it.
3931  //
3932  // Also if the new account is not signed by the server, I don't want to sign
3933  // it either. Need to check for all these things. Right now just proof of
3934  // concept.
3935 
3936  // Also, assuming all the verification shit is done here, I will have the
3937  // Nym
3938  // Wait a second, I think I have the Nym already cause there's a pointer on
3939  // the server connection that was passed in here...
3940 
3941  if (theReply.m_bSuccess &&
3942  theReply.m_strCommand.Compare("@triggerClause")) {
3943  OTIdentifier RECENT_HASH;
3944  const std::string str_server(strServerID.Get());
3945 
3946  if (theReply.m_strNymboxHash.Exists()) {
3947  RECENT_HASH.SetString(theReply.m_strNymboxHash);
3948 
3949  const bool bRecentHash =
3950  pNym->SetRecentHash(str_server, RECENT_HASH);
3951 
3952  if (!bRecentHash)
3953  otErr << theReply.m_strCommand
3954  << ": Failed getting NymboxHash (to store as 'recent "
3955  "hash') from Nym for server: " << str_server << "\n";
3956  else {
3957  OTPseudonym* pSignerNym = pNym;
3958  pNym->SaveSignedNymfile(*pSignerNym);
3959  }
3960  }
3961 
3962  return true;
3963  }
3964  if (theReply.m_bSuccess && theReply.m_strCommand.Compare("@getRequest")) {
3965  int64_t lNewRequestNumber = theReply.m_lNewRequestNum;
3966 
3967  // so the proper request number is sent next time, we take the one that
3968  // the server just sent us, and we ask the wallet to save it somewhere
3969  // safe
3970  // (like in the nymfile)
3971 
3972  // In the future, I will have to write a function on the wallet that
3973  // actually
3974  // takes the reply, looks up the associated nym in the wallet, verifies
3975  // that it was EXPECTING a response to GetRequest, (cause otherwise it
3976  // won't
3977  // know which one to update) and then updates the request number there.
3978  // In the meantime there is only one connection, and it already has a
3979  // pointer to
3980  // the Nym, so I'll just tell it to update the request number that way
3981  // for now.
3982 
3983  theConnection.OnServerResponseToGetRequestNumber(lNewRequestNumber);
3984 
3985  OTIdentifier RECENT_HASH;
3986  const std::string str_server(strServerID.Get());
3987 
3988  if (theReply.m_strNymboxHash.Exists()) {
3989  RECENT_HASH.SetString(theReply.m_strNymboxHash);
3990 
3991  const bool bRecentHash =
3992  pNym->SetRecentHash(str_server, RECENT_HASH);
3993 
3994  if (!bRecentHash)
3995  otErr << theReply.m_strCommand
3996  << ": Failed getting NymboxHash (to store as 'recent "
3997  "hash') from Nym for server: " << str_server << "\n";
3998  else {
3999  OTPseudonym* pSignerNym = pNym;
4000  pNym->SaveSignedNymfile(*pSignerNym);
4001  }
4002  }
4003 
4004  return true;
4005  }
4006  if (theReply.m_bSuccess && theReply.m_strCommand.Compare("@checkUser")) {
4007  const OTString strNymID2(theReply.m_strNymID2),
4008  strPubkey(theReply.m_strNymPublicKey.Get()); // Old style (It's
4009  // deprecated to pass a
4010  // pubkey directly like
4011  // this.)
4012 
4013  // First try to get Credentials, if there are any.
4014  //
4015  OTASCIIArmor& ascArmor =
4016  theReply.m_ascPayload; // credentialList (New style! Credentials.)
4017  OTASCIIArmor& ascArmor2 = theReply.m_ascPayload2; // credentials
4018  const bool bHasCredentials = (ascArmor.Exists() && ascArmor2.Exists());
4019  if (bHasCredentials) // New style of doing things, for Nym keys.
4020  // Credentials!
4021  {
4022  // credentialList
4023  //
4024  OTString strCredentialList;
4025  ascArmor.GetString(strCredentialList);
4026 
4027  if (strCredentialList.Exists()) {
4028  std::unique_ptr<OTDB::Storable> pStorable(OTDB::DecodeObject(
4029  OTDB::STORED_OBJ_STRING_MAP, ascArmor2.Get()));
4030  OTDB::StringMap* pMap =
4031  dynamic_cast<OTDB::StringMap*>(pStorable.get());
4032  if (nullptr == pMap)
4033  otOut << __FUNCTION__ << ": Failed decoding StringMap "
4034  "object in @checkUser.\n";
4035  else // IF the list saved, then we save the credentials
4036  // themselves...
4037  {
4038  OTString::Map& theMap = pMap->the_map;
4039  OTPseudonym theTargetNym;
4040  theTargetNym.SetIdentifier(strNymID2);
4041 
4042  if (false ==
4043  theTargetNym.LoadFromString(strCredentialList,
4044  &theMap)) {
4045  otErr << __FUNCTION__
4046  << ": @checkUser: Failure loading nym "
4047  << strNymID2 << " from credential string.\n";
4048  }
4049  // Now that the Nym has been loaded up from the message
4050  // parameters,
4051  // including the list of credential IDs, and the map
4052  // containing the
4053  // credentials themselves, let's try to Verify the
4054  // pseudonym. If we
4055  // verify, then we're safe to save the credentials to
4056  // storage.
4057  //
4058  else if (!theTargetNym.VerifyPseudonym()) {
4059  otErr << __FUNCTION__ << ": @checkUser: Loaded nym "
4060  << strNymID2 << " from credentials, but then it "
4061  "failed verifying.\n";
4062  }
4063  else // Okay, we loaded the Nym up from the credentials in
4064  // the message, AND
4065  { // verified the Nym (including the credentials.)
4066  // So let's save it to local storage...
4067  //
4068  std::string str_nym_id = strNymID2.Get();
4069  OTString strFilename;
4070  strFilename.Format("%s.cred", str_nym_id.c_str());
4071 
4072  bool bStoredList = false;
4073  OTString strOutput;
4074  if (ascArmor.Exists() &&
4075  ascArmor.WriteArmoredString(
4076  strOutput,
4077  "CREDENTIAL LIST") && // bEscaped=false by
4078  // default.
4079  strOutput.Exists())
4080  bStoredList = OTDB::StorePlainString(
4081  strOutput.Get(), OTFolders::Pubcred().Get(),
4082  strFilename.Get());
4083  if (!bStoredList)
4084  otErr << __FUNCTION__
4085  << ": Failed trying to armor or store "
4086  << strFilename << ".\n";
4087  else {
4088  otOut << "@checkUser: Success saving public "
4089  "credential list for Nym: " << strNymID2
4090  << "\n";
4091  for (auto& it : theMap) {
4092  std::string str_cred_id = it.first;
4093  OTString strCredential(it.second);
4094  bool bStoredCredential = false;
4095  strOutput.Release();
4096  OTASCIIArmor ascLoopArmor(strCredential);
4097  if (ascLoopArmor.Exists() &&
4098  ascLoopArmor.WriteArmoredString(
4099  strOutput,
4100  "CREDENTIAL") && // bEscaped=false by
4101  // default.
4102  strOutput.Exists())
4103  bStoredCredential = OTDB::StorePlainString(
4104  strOutput.Get(),
4105  OTFolders::Pubcred().Get(), str_nym_id,
4106  str_cred_id);
4107  if (!bStoredCredential)
4108  otErr << __FUNCTION__
4109  << ": Failed trying to store "
4110  "credential " << str_cred_id
4111  << " for nym " << str_nym_id << ".\n";
4112  else
4113  otOut
4114  << "@checkUser: Success saving public "
4115  "credential ID: " << str_cred_id
4116  << "\n";
4117  }
4118  } // Success decoding string map of credential contents.
4119  }
4120  }
4121  } // credential list exists, after base64-decoding.
4122  } // Has Credentials.
4123  // Old-style (deprecated.)
4124  //
4125  else if (strPubkey.Exists()) {
4126  OTString strPath = strNymID2.Get();
4127  // Next we save the public key in the pubkeys folder...
4128  //
4129  OTPseudonym thePubkeyNym(strNymID2);
4130 
4131  if (thePubkeyNym.SetPublicKey(strPubkey) &&
4132  thePubkeyNym.VerifyPseudonym()) {
4133  if (thePubkeyNym.SavePublicKey(strPath))
4134  otOut << "@checkUser: (Deprecated.) Success saving public "
4135  "key file for Nym: " << strNymID2 << "\n";
4136  }
4137  }
4138 
4139  return true;
4140  }
4141  else if (theReply.m_bSuccess &&
4142  theReply.m_strCommand.Compare("@notarizeTransactions")) {
4143  otOut << "Received server response to notarize Transactions message.\n";
4144  // otOut << "Received server response to notarize
4145  // Transactions message:\n" << strReply << "\n";
4146  OTIdentifier RECENT_HASH;
4147  const std::string str_server(strServerID.Get());
4148 
4149  if (theReply.m_strNymboxHash.Exists()) {
4150  RECENT_HASH.SetString(theReply.m_strNymboxHash);
4151 
4152  const bool bRecentHash =
4153  pNym->SetRecentHash(str_server, RECENT_HASH);
4154 
4155  if (!bRecentHash)
4156  otErr << theReply.m_strCommand
4157  << ": Failed getting NymboxHash (to store as 'recent "
4158  "hash') from Nym for server: " << str_server << "\n";
4159  else {
4160  OTPseudonym* pSignerNym = pNym;
4161  pNym->SaveSignedNymfile(*pSignerNym);
4162  }
4163  }
4164  ProcessIncomingTransactions(theConnection, theReply);
4165 
4166  // todo (gui):
4167  // This block assumes that the above "@notarizeTransactions", being
4168  // successful, probably changed
4169  // the account balance. A nice GUI would probably interpret the reply
4170  // and edit the local files
4171  // to update them to match (since it was successful). In fact, the above
4172  // call to ProcessIncomingTransactions
4173  // does some of that sort of stuff already, at least for issued numbers
4174  // on the nym.
4175  //
4176  // (For now we just re-download the files.)
4177 
4178  return true;
4179  }
4180  else if (theReply.m_bSuccess &&
4181  theReply.m_strCommand.Compare("@getTransactionNum")) {
4182  otOut << "Received server response to Get Transaction Num message.\n";
4183  // otOut << "Received server response to Get Transaction
4184  // Num message:\n" << strReply << "\n";
4185 
4186  OTIdentifier RECENT_HASH;
4187  const std::string str_server(strServerID.Get());
4188 
4189  if (theReply.m_strNymboxHash.Exists()) {
4190  RECENT_HASH.SetString(theReply.m_strNymboxHash);
4191 
4192  const bool bRecentHash =
4193  pNym->SetRecentHash(str_server, RECENT_HASH);
4194 
4195  if (!bRecentHash)
4196  otErr << theReply.m_strCommand
4197  << ": Failed getting NymboxHash (to store as 'recent "
4198  "hash') from Nym for server: " << str_server << "\n";
4199  else {
4200  OTPseudonym* pSignerNym = pNym;
4201  pNym->SaveSignedNymfile(*pSignerNym);
4202  }
4203  }
4204  return true;
4205  }
4206  else if (theReply.m_bSuccess &&
4207  theReply.m_strCommand.Compare("@getNymbox")) {
4208  OTString strReply(theReply);
4209 
4210  otOut << "Received @getNymbox server response ("
4211  << (theReply.m_bSuccess ? "success" : "failure") << ")\n";
4212 
4213  // base64-Decode the server reply's payload into strInbox
4214  OTString strNymbox(theReply.m_ascPayload);
4215 
4216  // IF pNymbox NOT nullptr, THEN USE IT INSTEAD OF LOADING MY OWN.
4217  // Except... @getNymbox isn't dropped as a replyNotice into the Nymbox,
4218  // so we'll never end up here except in cases where it needs to be
4219  // loaded. I can even ASSERT here, that the pointer is actually nullptr!
4220  //
4221  OT_ASSERT_MSG(nullptr == pNymbox, "Nymbox pointer is expected to be "
4222  "nullptr here, since @getNymbox "
4223  "isn't dropped as a server "
4224  "replyNotice into the nymbox.");
4225 
4226  // Load the ledger object from that string.
4227  OTLedger theNymbox(USER_ID, USER_ID, SERVER_ID);
4228 
4229  OTIdentifier NYMBOX_HASH, RECENT_HASH;
4230  const std::string str_server(strServerID.Get());
4231 
4232  if (theReply.m_strNymboxHash.Exists()) {
4233  NYMBOX_HASH.SetString(theReply.m_strNymboxHash);
4234  RECENT_HASH.SetString(theReply.m_strNymboxHash);
4235 
4236  const bool bNymboxHash =
4237  pNym->SetNymboxHash(str_server, NYMBOX_HASH);
4238  const bool bRecentHash =
4239  pNym->SetRecentHash(str_server, RECENT_HASH);
4240 
4241  if (!bNymboxHash)
4242  otErr << "Failed setting NymboxHash on Nym for server: "
4243  << str_server << "\n";
4244  if (!bRecentHash)
4245  otErr << theReply.m_strCommand
4246  << ": Failed setting NymboxHash (to store as 'recent "
4247  "hash') from Nym for server: " << str_server << "\n";
4248  if (bNymboxHash || bRecentHash) {
4249  OTPseudonym* pSignerNym = pNym;
4250  pNym->SaveSignedNymfile(*pSignerNym);
4251  }
4252  }
4253 
4254  // I receive the nymbox, verify the server's signature, then RE-SIGN IT
4255  // WITH MY OWN
4256  // SIGNATURE, then SAVE it to local storage. So any FUTURE checks of
4257  // this nymbox
4258  // would require MY signature, not the server's, to verify. But in this
4259  // one spot,
4260  // just before saving, I need to verify the server's first.
4261  // UPDATE: Keeping the server's signature, and just adding my own.
4262  //
4263  if (theNymbox.LoadNymboxFromString(
4264  strNymbox)) // && theNymbox.VerifyAccount(*pServerNym)) No point
4265  // doing this, since the client hasn't even had a
4266  // chance to download the box receipts yet.
4267  // (VerifyAccount will fail before then...)
4268  {
4269 
4270  //
4271  // UPDATE: We will have to rely on the Developer using the OT API to
4272  // call
4273  // OT_API_FlushSentMessages IMMEDIATELY after calling getNymbox and
4274  // receiving
4275  // a successful reply. Why? Because that's the only way to give him
4276  // the chance
4277  // to see if certain replies are there or not (before they get
4278  // removed.) That way
4279  // he can do his own harvesting, do a re-try, etc and then finally
4280  // when he is done
4281  // with that, do the flush.
4282  //
4283 
4284  theNymbox.ReleaseSignatures(); // Now I'm keeping the server
4285  // signature, and just adding my own.
4286  theNymbox.SignContract(*pNym); // UPDATE: Releasing the signature
4287  // again, since Receipts are now
4288  // fully functional.
4289  theNymbox.SaveContract(); // Thus we can prove the Nymbox using the
4290  // last signed transaction receipt. This
4291  // means
4292  theNymbox.SaveNymbox(); // the receipt is our proof, and the nymbox
4293  // becomes just an intermediary file that is
4294  // downloaded occasionally (like checking for new email) but no
4295  // trust is risked since
4296  // the downloaded file is always verified against the receipt!
4297  }
4298  else {
4299  otErr << "OTClient::ProcessServerReply: Error loading or verifying "
4300  "nymbox during @getNymbox:\n\n" << strNymbox << "\n";
4301  }
4302 
4303  return true;
4304  }
4305  else if (theReply.m_bSuccess &&
4306  theReply.m_strCommand.Compare("@getBoxReceipt")) {
4307  // OTString strReply(theReply);
4308  otOut << "Received server response to getBoxReceipt request ("
4309  << (theReply.m_bSuccess ? "success" : "failure") << ")\n";
4310 
4311  // IF pNymbox NOT nullptr, THEN USE IT INSTEAD OF LOADING MY OWN.
4312  // Except... @getNymbox isn't dropped as a replyNotice into the Nymbox,
4313  // so we'll never end up here except in cases where it needs to be
4314  // loaded. I can even ASSERT here, that the pointer is actually nullptr!
4315  //
4316  OT_ASSERT_MSG(nullptr == pNymbox, "Nymbox pointer is expected to be "
4317  "nullptr here, since @getBoxReceipt "
4318  "isn't dropped as a server "
4319  "replyNotice into the nymbox.");
4320 
4321  // Note: I don't HAVE to load the ledger, and what if there are 500000
4322  // receipts in it?
4323  // Do I want to reload it EVERY time? Therefore
4324  bool bErrorCondition = false;
4325  bool bSuccessLoading =
4326  true; // We don't need to load the ledger, so that's commented out.
4327 
4328  switch (theReply.m_lDepth) { // No need to load the ledger at this
4329  // point... plus, it would slow things
4330  // down.
4331  case 0: // bSuccessLoading = pLedger->LoadNymbox(); break;
4332  case 1: // bSuccessLoading = pLedger->LoadInbox(); break;
4333  case 2: // bSuccessLoading = pLedger->LoadOutbox(); break;
4334  break;
4335  default:
4336  otErr << __FUNCTION__
4337  << ": @getBoxReceipt: Unknown box type: " << theReply.m_lDepth
4338  << "\n";
4339  bErrorCondition = true;
4340  break;
4341  }
4342 
4343  if (bSuccessLoading && !bErrorCondition)
4344  // && pLedger->VerifyAccount(*pServerNym)) // commenting
4345  // this out for now -- unnecessary. Plus, it speeds things up to
4346  // remove this.
4347  {
4348  // At this point, the ledger is loaded. Now let's use it for what we
4349  // really
4350  // wanted: To save the Box Receipt!
4351  // Update: not loading ledger -- it would slow things down. Added a
4352  // method that allowed me to circumvent loading it.
4353 
4354  // base64-Decode the server reply's payload into strTransaction
4355  //
4356  const OTString strTransType(theReply.m_ascPayload);
4357  std::unique_ptr<OTTransactionType> pTransType;
4358 
4359  if (strTransType.Exists())
4360  pTransType.reset(
4362 
4363  if (nullptr == pTransType)
4364  otErr << __FUNCTION__
4365  << ": @getBoxReceipt: Error instantiating transaction "
4366  "type based on decoded theReply.m_ascPayload:\n\n"
4367  << strTransType << "\n";
4368  else {
4369  OTTransaction* pBoxReceipt =
4370  dynamic_cast<OTTransaction*>(pTransType.get());
4371 
4372  if (nullptr == pBoxReceipt)
4373  otErr << __FUNCTION__
4374  << ": @getBoxReceipt: Error dynamic_cast from "
4375  "transaction type to transaction, based on "
4376  "decoded theReply.m_ascPayload:\n\n"
4377  << strTransType << "\n\n";
4378  else if (!pBoxReceipt->VerifyAccount(*pServerNym))
4379  otErr << __FUNCTION__
4380  << ": @getBoxReceipt: Error: Box Receipt "
4381  << pBoxReceipt->GetTransactionNum() << " in "
4382  << ((theReply.m_lDepth == 0)
4383  ? "nymbox"
4384  : ((theReply.m_lDepth == 1) ? "inbox"
4385  : "outbox"))
4386  << " fails VerifyAccount().\n"; // outbox is 2.);
4387  else if (pBoxReceipt->GetTransactionNum() !=
4388  theReply.m_lTransactionNum)
4389  otErr << __FUNCTION__
4390  << ": @getBoxReceipt: Error: Transaction Number "
4391  "doesn't match on the box receipt itself ("
4392  << pBoxReceipt->GetTransactionNum()
4393  << "), versus the one listed in the reply message ("
4394  << theReply.m_lTransactionNum << ").\n";
4395  // Note: Account ID and Server ID were already verified, in
4396  // VerifyAccount().
4397  else if (pBoxReceipt->GetUserID() != USER_ID) {
4398  const OTString strPurportedUserID(pBoxReceipt->GetUserID());
4399  otErr << __FUNCTION__
4400  << ": @getBoxReceipt: Error: NymID doesn't match on "
4401  "the box receipt itself (" << strPurportedUserID
4402  << "), versus the one listed in the reply message ("
4403  << theReply.m_strNymID << ").\n";
4404  }
4405  else // FINALLY we have the Ledger AND the Box Receipt both
4406  // loaded at the same time.
4407  { // UPDATE: Not loading the ledger at this point. Not
4408  // necessary. Faster without it.
4409 
4410  // UPDATE: We will ASSUME the abbreviated receipt is in the
4411  // NYMBOX, which is WHY
4412  // we are now downloading the FULL BOX RECEIPT. We will SAVE
4413  // it for the Nymbox,
4414  // which finishes the Nymbox (already in box as abbreviated,
4415  // and already saved in full
4416  // in box receipts folder). Next we will also add it to the
4417  // PAYMENT INBOX and RECORD BOX,
4418  // if it's the right sort of receipt. We will also save
4419  // THEIR versions of the FULL BOX RECEIPT,
4420  // just as we did for the Nymbox here.
4421 
4423  pBoxReceipt->GetType()) ||
4425  pBoxReceipt->GetType())) {
4426  // Just make sure not to add it if it's already there...
4427  if (!strServerID.Exists()) {
4428  otErr << __FUNCTION__
4429  << ": strServerID doesn't Exist!\n";
4430  OT_FAIL;
4431  }
4432  if (!strNymID.Exists()) {
4433  otErr << __FUNCTION__
4434  << ": strNymID dosn't Exist!\n";
4435  OT_FAIL;
4436  }
4437  const bool bExists =
4439  strServerID.Get(), strNymID.Get());
4440  OTLedger thePmntInbox(USER_ID, USER_ID,
4441  SERVER_ID); // payment inbox
4442  bool bSuccessLoading =
4443  (bExists && thePmntInbox.LoadPaymentInbox());
4444  if (bExists && bSuccessLoading)
4445  bSuccessLoading =
4446  (thePmntInbox.VerifyContractID() &&
4447  thePmntInbox.VerifySignature(*pNym));
4448  // bSuccessLoading =
4449  // (thePmntInbox.VerifyAccount(*pNym)); // (No need here
4450  // to load all the Box Receipts by using VerifyAccount)
4451  else if (!bExists)
4452  bSuccessLoading = thePmntInbox.GenerateLedger(
4453  USER_ID, SERVER_ID, OTLedger::paymentInbox,
4454  true); // bGenerateFile=true
4455  // by this point, the nymbox DEFINITELY exists -- or
4456  // not. (generation might have failed, or verification.)
4457 
4458  if (!bSuccessLoading) {
4459  OTString strUserID(USER_ID), strAcctID(USER_ID);
4460  otOut << __FUNCTION__
4461  << ": @getBoxReceipt: WARNING: Unable to "
4462  "load, verify, or generate paymentInbox, "
4463  "with IDs: " << strUserID << " / "
4464  << strAcctID << "\n";
4465  }
4466  else // --- ELSE --- Success loading the payment inbox
4467  // and recordBox and verifying their contractID
4468  // and signature, (OR success generating the
4469  // ledger.)
4470  {
4471  // The transaction (which we are putting into the
4472  // payment inbox) will not
4473  // be removed from the nymbox until we receive the
4474  // server's success reply to
4475  // this "process Nymbox" message. That's why you see
4476  // me adding it here to
4477  // the payment inbox, while not removing it from the
4478  // Nymbox (because that
4479  // will happen once the reply is received.) NOTE:
4480  // Need to make sure the
4481  // associated box receipt doesn't get MARKED FOR
4482  // DELETION when being removed
4483  // at that time.
4484  //
4485  // void
4486  // load_str_trans_add_to_ledger(const OTIdentifier&
4487  // the_nym_id, const OTString& str_trans, const
4488  // OTString str_box_type, const int64_t& lTransNum,
4489  // OTPseudonym& the_nym, OTLedger& ledger);
4490 
4491  // Basically we are taking this receipt from the
4492  // Nymbox, and also adding copies of it
4493  // to the paymentInbox and the recordBox.
4494  //
4495  // QUESTION: what if I ERASE it out of my recordBox.
4496  // Won't it pop back up again?
4497  // ANSWER: YES, but not if I do this instead at
4498  // @getBoxReceipt which will only happen once.
4499  // UPDATE: which I now AM (see our location
4500  // here...)
4501  // HOWEVER: Most likely not, because this notice
4502  // will no longer BE in my Nymbox...
4503  //
4504  // QUESTION: What if I ERASE it out of my
4505  // paymentInbox? Won't this pop back there again?
4506  // ANSWER: I can't erase it out of there. I can
4507  // either accept it or reject it. Either way,
4508  // it is removed from my paymentInbox at that time
4509  // by OT. Like above, if a copy were still
4510  // in the Nymbox, I would get a duplicate here when
4511  // processing Nymbox again. But MOST TIMES,
4512  // there will be no duplicate, because it will
4513  // already be cleaned out of my Nymbox anyway.
4514  //
4515  //
4516  const int64_t lTransNum =
4517  pBoxReceipt->GetTransactionNum();
4518 
4519  // If pBoxReceipt->GetType() is instrument notice,
4520  // add to the payments inbox.
4521  // (It will be moved to record box after the
4522  // incoming payment is deposited or discarded.)
4523  //
4525  USER_ID, strTransType, "paymentInbox",
4526  lTransNum, *pNym, thePmntInbox);
4527  // load_str_trans_add_to_ledger(USER_ID,
4528  // strTransType, "recordBox", lTransNum, *pNym,
4529  // theRecordBox); // No longer here. Moved to
4530  // processDepositResponse
4531 
4532  } // --- ELSE --- Success loading the payment inbox and
4533  // verifying its contractID and signature, OR success
4534  // generating the ledger.
4535  } // if pBoxReceipt is instrumentNotice or
4536  // instrumentRejection...
4537 
4538  // pBoxReceipt->ReleaseSignatures();
4539 
4540  // I don't release the server's signature, so later on I can
4541  // verify either
4542  // signature -- the server's or pNym's. Both should be on
4543  // the receipt.
4544  // UPDATE: We're not changing the content of the Box Receipt
4545  // AT ALL
4546  // because we don't want to already its message digest,
4547  // which will be
4548  // compared to the hash stored in the abbreviated version of
4549  // the same receipt.
4550  //
4551  // pBoxReceipt->SignContract(*pNym);
4552  // pBoxReceipt->SaveContract();
4553 
4554  // if
4555  // (!pBoxReceipt->SaveBoxReceipt(*pLedger))
4556  // // <===================
4557  if (!pBoxReceipt->SaveBoxReceipt(
4558  theReply.m_lDepth)) // <===================
4559  otErr << __FUNCTION__
4560  << ": @getBoxReceipt(): Failed trying to "
4561  "SaveBoxReceipt. Contents:\n\n" << strTransType
4562  << "\n\n";
4563  /* theReply.m_lDepth in this context stores boxType. Value
4564  * can be: 0/nymbox,1/inbox,2/outbox*/
4565 
4566  } // We can save the box receipt.
4567  } // Success loading the boxReceipt from the server reply
4568  } // No error condition.
4569  else {
4570  otErr << __FUNCTION__
4571  << ": SHOULD NEVER HAPPEN: @getBoxReceipt: failure loading "
4572  "box, or verifying it. UserID: " << theReply.m_strNymID
4573  << " AcctID: " << theReply.m_strAcctID << " \n";
4574  }
4575 
4576  return true;
4577 
4578  } // @getBoxReceipt
4579  // IN EITHER of these cases, the number of transaction numbers on my Nym has
4580  // probably changed.
4581  // But the server acknowledgment here confirms it, so I should remove any
4582  // issued numbers,
4583  // save the nym, etc.
4584  //
4585  else if (theReply.m_bSuccess &&
4586  (theReply.m_strCommand.Compare("@processInbox") ||
4587  theReply.m_strCommand.Compare("@processNymbox"))) {
4588  OTString strServerID(SERVER_ID), strReply(theReply);
4589 
4590  otOut << "Received server response: " << theReply.m_strCommand << " \n";
4591  // otOut << "Received server response to processInbox or
4592  // processNymbox message:\n" << strReply << "\n";
4593  OTIdentifier RECENT_HASH;
4594  const std::string str_server(strServerID.Get());
4595 
4596  if (theReply.m_strNymboxHash.Exists()) {
4597  RECENT_HASH.SetString(theReply.m_strNymboxHash);
4598 
4599  const bool bRecentHash =
4600  pNym->SetRecentHash(str_server, RECENT_HASH);
4601 
4602  if (!bRecentHash)
4603  otErr << theReply.m_strCommand
4604  << ": Failed getting NymboxHash (to store as 'recent "
4605  "hash') from Nym for server: " << str_server << "\n";
4606  else {
4607  OTPseudonym* pSignerNym = pNym;
4608  pNym->SaveSignedNymfile(*pSignerNym);
4609  }
4610  }
4611  // If the server acknowledges either of the above commands, then my
4612  // transaction
4613  // numbers have changed. I need to read the numbers from my last
4614  // transaction agreement
4615  // (which should be saved in this server reply) and make sure to update
4616  // my nym accordingly.
4617  //
4618  OTString strOriginalMessage;
4619  if (theReply.m_ascInReferenceTo.Exists())
4620  theReply.m_ascInReferenceTo.GetString(strOriginalMessage);
4621 
4622  OTMessage theOriginalMessage;
4623 
4624  if (strOriginalMessage.Exists() &&
4625  theOriginalMessage.LoadContractFromString(strOriginalMessage) &&
4626  theOriginalMessage.VerifySignature(*pNym)) {
4627  OTString strLedger, strReplyLedger;
4628 
4629  if (theReply.m_strCommand.Compare("@processNymbox"))
4630  ACCOUNT_ID = USER_ID; // For Nymbox, UserID *is* AcctID.
4631 
4632  OTLedger theLedger(USER_ID, ACCOUNT_ID, SERVER_ID),
4633  theReplyLedger(USER_ID, ACCOUNT_ID, SERVER_ID);
4634 
4635  theOriginalMessage.m_ascPayload.GetString(strLedger);
4636  theReply.m_ascPayload.GetString(strReplyLedger);
4637 
4638  if (!strLedger.Exists()) {
4639  OTString strLogData(theOriginalMessage);
4640  otErr << "Strange: Received server acknowledgment ("
4641  << theReply.m_strCommand
4642  << "), but found no request ledger within your original "
4643  "message:\n\n" << strLogData << "\n\n";
4644  }
4645  else if (!strReplyLedger.Exists()) {
4646  OTString strReply(theReply);
4647  otOut << "Strange... received server acknowledgment ("
4648  << theReply.m_strCommand
4649  << "), but found no reply ledger within:\n\n" << strReply
4650  << "\n\n";
4651  }
4652  else if (!theLedger.LoadLedgerFromString(strLedger)) {
4653  otErr << "Strange: Received server acknowledgment ("
4654  << theReply.m_strCommand
4655  << "), but unable to load original request ledger from "
4656  "string:\n\n" << strLedger << "\n\n";
4657  }
4658  else if (!theLedger.VerifySignature(*pNym)) {
4659  otErr << "Strange: Received server acknowledgment ("
4660  << theReply.m_strCommand
4661  << "), but unable to verify your signature on the "
4662  "original request ledger:\n\n" << strLedger << "\n\n";
4663  }
4664  else if (!theReplyLedger.LoadLedgerFromString(strReplyLedger)) {
4665  otErr
4666  << "Strange: Received server acknowledgment ("
4667  << theReply.m_strCommand
4668  << "), but unable to load the reply ledger from string:\n\n"
4669  << strReplyLedger << "\n\n";
4670  }
4671  else if (!theReplyLedger.VerifySignature(*pServerNym)) {
4672  otErr << "Strange: Received server acknowledgment ("
4673  << theReply.m_strCommand
4674  << "), but unable to verify server's signature on the "
4675  "reply ledger within:\n\n" << strReplyLedger << "\n\n";
4676  }
4677  else {
4678  // atAcceptItemReceipt: Whether success or fail, remove the
4679  // number used from list of responsibility.
4680  // ALSO, if success, remove the number from
4681  // the original cheque or the original transfer request.
4682  //
4683  // Other options are not handled here, but they ARE handled
4684  // elsewhere (above). They are:
4685  //
4686  // atDeposit: Whether success or fail, remove the number
4687  // from my list of responsibility.
4688  // atWithdrawal: Whether success or fail, remove the number
4689  // from my list of responsibility.
4690  // atAcceptPending: Whether success or fail, remove the
4691  // number from my list of responsibility.
4692  // atTransfer: If success, KEEP the number on my issued
4693  // list. (Remove when transfer receipt is accepted.)
4694  // If failure, REMOVE the number from my
4695  // issued list. (Use a new one next time.)
4696  // atMarketOffer: If success, KEEP the number on my issued
4697  // list. (Removed when final receipt is created.)
4698  // If failure, REMOVE the number from my
4699  // issued list. (Use a new one next time.)
4700  // atCancelCronItem: Whether success or fail, remove the number
4701  // from my list of responsibility.
4702  // atExchangeBasket: Whether success or fail, remove the number
4703  // from my list of responsibility.
4704 
4705  OTTransaction* pTransaction = nullptr;
4706  OTTransaction* pReplyTransaction = nullptr;
4707 
4708  if (theReply.m_strCommand.Compare(
4709  "@processInbox")) // We're processing the SERVER's REPLY
4710  // to our processInbox request.
4711  {
4712  pTransaction =
4713  theLedger.GetTransaction(OTTransaction::processInbox);
4714  pReplyTransaction = theReplyLedger.GetTransaction(
4716 
4717  if (nullptr != pTransaction) {
4718  // pNym->RemoveTransactionNum() happened whenever I
4719  // first fired off the processInbox request.
4720  // Now let's remove that number from our ISSUED list of
4721  // responsibility, since we got a server reply...
4722  // <====> Whatever trans num I used to process inbox is
4723  // now OFF my issued list on server side!
4724  // (Therefore remove here too, to match..)
4725  //
4726  const bool bIsSignedOut = pNym->VerifyIssuedNum(
4727  strServerID, pTransaction->GetTransactionNum());
4728 
4729  // Why? Because we might have already processed this,
4730  // when it first happened, and now we're just
4731  // seeing a repeat of the message from a nymbox notice.
4732  // (Some messages are so important, you get
4733  // a nymbox notice including a copy of the message, so
4734  // the server can make SURE you have processed
4735  // the reply. This was added to prevent syncing issues
4736  // between client and server.)
4737  //
4738  if (bIsSignedOut)
4739  pNym->RemoveIssuedNum(
4740  *pNym, strServerID,
4741  pTransaction->GetTransactionNum(),
4742  true); // bool bSave=true
4743  if (bIsSignedOut && (nullptr != pReplyTransaction)) {
4744  // Load the inbox.
4745  OTLedger theInbox(USER_ID, ACCOUNT_ID, SERVER_ID);
4746  OTLedger theRecordBox(USER_ID, ACCOUNT_ID,
4747  SERVER_ID);
4748 
4749  bool bInbox = OTDB::Exists(
4750  OTFolders::Inbox().Get(), strServerID.Get(),
4751  theReply.m_strAcctID.Get());
4752 
4753  if (bInbox && theInbox.LoadInbox())
4754  bInbox = theInbox.VerifyAccount(*pNym);
4755 
4756  // I JUST had this loaded if I sent acceptWhatever
4757  // just instants ago, (which I am now processing the
4758  // reply for.)
4759  // Therefore I'm just ASSUMING here that it loads
4760  // successfully here, since it worked an instant
4761  // ago. Todo.
4762  OT_ASSERT_MSG(bInbox,
4763  "Was trying to load / verify Inbox.");
4764  bool bLoadedRecordBox = false;
4765  bool bRecordBoxExists = OTDB::Exists(
4766  OTFolders::RecordBox().Get(), strServerID.Get(),
4767  theReply.m_strAcctID.Get());
4768  // Next, loop through the reply items for each
4769  // "process inbox" item that I must have previously
4770  // sent.
4771  // For each, if successful, remove from inbox.
4772  // For item receipts, if successful, also remove the
4773  // appropriate trans#
4774  // from my issued list of transaction numbers (like
4775  // above.)
4776 
4777  for (auto& it_bigloop :
4778  pReplyTransaction->GetItemList()) {
4779  OTItem* pReplyItem = it_bigloop;
4780  OT_ASSERT_MSG(nullptr != pReplyItem,
4781  "OTClient::ProcessServerReply: "
4782  "Pointer should not have been "
4783  "nullptr.");
4784 
4785  // otErr << " *** TOP OF LOOP of Reply items,
4786  // one presumably for each processInbox that I
4787  // sent previously.\n";
4788 
4789  OTItem::itemType theItemType =
4791 
4792  switch (pReplyItem->GetType()) {
4794  theItemType = OTItem::acceptPending;
4795  break;
4797  theItemType = OTItem::acceptCronReceipt;
4798  break;
4800  theItemType = OTItem::acceptItemReceipt;
4801  break;
4802 
4803  case OTItem::atRejectPending: // turn down the
4804  // money!
4805  theItemType = OTItem::rejectPending;
4806  continue; // unused
4807  case OTItem::atDisputeCronReceipt: // dispute a
4808  // market
4809  // trade or
4810  // payment
4811  // for a
4812  // payment plan
4813  theItemType = OTItem::disputeCronReceipt;
4814  continue; // unused
4815  case OTItem::atDisputeItemReceipt: // dispute a
4816  // cheque
4817  // receipt or
4818  // transfer
4819  // receipt.
4820  theItemType = OTItem::disputeItemReceipt;
4821  continue; // unused
4822 
4824  theItemType = OTItem::acceptFinalReceipt;
4825  break;
4826 
4828  theItemType = OTItem::acceptBasketReceipt;
4829  break;
4830 
4832  theItemType = OTItem::disputeFinalReceipt;
4833  continue; // unused
4835  theItemType = OTItem::disputeBasketReceipt;
4836  continue; // unused
4837 
4838  // We don't care about these here.
4839  //
4841  theItemType = OTItem::balanceStatement;
4842  continue;
4844  theItemType = OTItem::transactionStatement;
4845  continue;
4846 
4847  // FYI, on server side, it does not bother to
4848  // process an item,
4849  // if the balance statement or transaction
4850  // statement has not succeeded.
4851  //
4852  // Thus, if the ITEM ITSELF has succeeded, that
4853  // means the balance or
4854  // transaction statement MUST have succeeded!
4855  // Because server wouldn't have
4856  // even bothered to process the item otherwise.
4857  //
4858  // There still might be some future application
4859  // in doing something with these
4860  // statements when they come in.
4861 
4862  default: {
4863  const int32_t nReplyItemType =
4864  pReplyItem->GetType();
4865 
4866  OTString strTheType;
4867  pReplyItem->GetTypeString(strTheType);
4868 
4869  otErr << "*** Unexpected reply item type ("
4870  << nReplyItemType
4871  << ") in @processInbox, while "
4872  "processing server reply: "
4873  << strTheType << " \n";
4874  continue;
4875  }
4876  } // SWITCH
4877 
4878  // The below actions are only necessary if
4879  // pReplyItem was a SUCCESS.
4880  // (Otherwise we skip them...)
4881  //
4882  OTString strTempTypeString;
4883  pReplyItem->GetTypeString(strTempTypeString);
4884 
4886  pReplyItem->GetStatus()) {
4887  otWarn << "@processInbox reply item "
4888  << strTempTypeString
4889  << ": status == FAILED\n";
4890  continue;
4891  }
4892  // else
4893  otWarn << "@processInbox reply item "
4894  << strTempTypeString
4895  << ": status == SUCCESS\n";
4896 
4897  // WTF IS THIS? There could be 3 acceptPendings,
4898  // 5 acceptCronReceipts, 3 acceptFinalReceipts,
4899  // etc
4900  // in a single ProcessInbox transaction.
4901  // Therefore this "get by type" will NOT fly in
4902  // this case.
4903  // (Fixing this now to look it up by ID instead
4904  // of type.)
4905  //
4906  // OTItem * pItem =
4907  // pTransaction->GetItem(theItemType);
4908  //
4909  // Can't do this either:
4910  // OTItem * pItem =
4911  // pTransaction->GetItemInRefTo(pReplyItem->GetReferenceToNum());
4912  //
4913  // (pReplyItem->GetReferenceToNum() contains the
4914  // processInbox transaction# of pItem, not
4915  // the inbox receipt # that pItem is in
4916  // reference to.)
4917  //
4918  // pTransaction is the processInbox transaction
4919  // request that I sent.
4920  // (The items within it all share its same
4921  // transaction number, but they are IN REFERENCE
4922  // TO
4923  // the inbox receipts that they accept/reject.)
4924  // pReplyTransaction is the server's reply to
4925  // that.
4926  // pReplyItem is the current item when iterating
4927  // through pReplyTransaction.
4928  // pItem is the corresponding REQUEST item from
4929  // pTransaction, that pReplyItem is responding
4930  // to.
4931  //
4932  // Therefore: I need to load the original item
4933  // from pReplyItem's reference string (it's
4934  // bundled in there).
4935  // THEN I will get the "in reference to" number
4936  // from THAT (which is the inbox Receipt #).
4937  // THEN I will use that number to look up the
4938  // SAME original item from pTransaction.
4939  // The last step isn't technically necessary,
4940  // but may be useful for security.
4941  //
4942  // Sheesh!
4943 
4944  OTString strProcessInboxItem;
4945  pReplyItem->GetReferenceString(
4946  strProcessInboxItem);
4947 
4948  std::unique_ptr<OTItem> pProcessInboxItem(
4950  strProcessInboxItem, SERVER_ID,
4951  pReplyItem->GetReferenceToNum()));
4952 
4953  // pProcessInboxItem is already a copy of the
4954  // correct processInbox item that I need. But
4955  // still, it's a copy that the SERVER
4956  // sent me. So I'm going to use it to get the
4957  // reference number that I need, in order to
4958  // look up MY copy of the item.
4959  // So pItem is my original request, inside a
4960  // processInbox transaction, to accept some
4961  // receipt from my inbox.
4962  //
4963  OTItem* pItem =
4964  (pProcessInboxItem != nullptr)
4965  ? pTransaction->GetItemInRefTo(
4966  pProcessInboxItem
4967  ->GetReferenceToNum())
4968  : nullptr;
4969 
4970  if (nullptr == pItem) {
4971  otErr << "Unable to find original item in "
4972  "original processInbox "
4973  "transaction request, based on "
4974  "reply item.\n";
4975  continue;
4976  }
4977 
4978  // If this happens, it means the item we found
4979  // in our original process inbox transaction,
4980  // which matched the
4981  // "in reference to" number that we expected
4982  // from the copy of that original item we loaded
4983  // from within the
4984  // pReplyItem that's supposedly responding to
4985  // it, does not have the same TYPE that we would
4986  // have expected it to
4987  // have, based on the intelligence in the above
4988  // switch statement.
4989  //
4990  if (pItem->GetType() !=
4991  theItemType) { // (Possible types for pItem:
4992  // acceptItemReceipt,
4993  // acceptPending,
4994  // acceptCronReceipt,
4995  // acceptFinalReceipt,
4996  // acceptBasketReceipt.)
4997  otErr << "Wrong original item TYPE, on "
4998  "reply item's copy of original "
4999  "item, than what was expected "
5000  "based on reply item's type.\n";
5001  continue;
5002  }
5003 
5004  // Todo here: any other verification of pItem
5005  // against pProcessInboxItem, which are
5006  // supposedly copies of the same item.
5007 
5008  // FYI, pItem->GetReferenceToNum() is the ID of
5009  // the receipt that's in the inbox.
5010  //
5011 
5012  OTTransaction* pServerTransaction = nullptr;
5013 
5014  otWarn << "Checking client-side inbox for "
5015  "expected pending or receipt "
5016  "transaction: "
5017  << pItem->GetReferenceToNum()
5018  << "... \n"; // temp remove
5019 
5020  switch (pReplyItem->GetType()) {
5021  case OTItem::atAcceptPending: // Server reply to
5022  // my acceptance
5023  // of
5024  // pending transfer.
5025  case OTItem::atAcceptItemReceipt: // Server
5026  // reply to my
5027  // acceptance
5028  // of
5029  // chequeReceipt, voucherReceipt or
5030  // transferReceipt.
5031 
5032  pServerTransaction =
5033  theInbox.GetTransaction(
5034  pItem->GetReferenceToNum());
5035  break;
5039  pServerTransaction =
5040  theInbox.GetTransaction(
5041  pItem->GetReferenceToNum());
5042  break;
5043 
5044  default: {
5045  const int32_t nReplyItemType =
5046  pReplyItem->GetType();
5047 
5048  OTString strTheType;
5049  pReplyItem->GetTypeString(strTheType);
5050 
5051  otErr << "*** Unexpected reply item type ("
5052  << nReplyItemType
5053  << ") in @processInbox, while "
5054  "processing server reply: "
5055  << strTheType << "\n";
5056  break; // will return just below, where it
5057  // checks pServerTransaction for
5058  // nullptr.
5059  }
5060  }
5061 
5062  if (nullptr == pServerTransaction) {
5063  otErr << "Unable to find the server's "
5064  "receipt, in my inbox, that my "
5065  "original processInbox's item was "
5066  "referring to.\n";
5067  break; // We must've processed this already,
5068  // and it came through again cause a
5069  // copy was in a nymbox notice.
5070  }
5071 
5072  bool bAddToRecordBox = true;
5073 
5074  switch (pReplyItem->GetType()) // All of these
5075  // need to remove
5076  // something from
5077  // the
5078  // client-side
5079  // inbox. (Which
5080  // happens below
5081  // this switch.)
5082  { // Some also need to remove an issued
5083  // transaction number from pNym.
5085 
5086  break;
5087 
5088  // In the case of item receipt (not cron receipt
5089  // or pending) I need to
5090  // remove the issued num from my list of
5091  // responsibility. (Since I finally
5092  // accepted the receipt and closed it out.)
5093  //
5094  // (Basically closing out the original transfer
5095  // I must have sent, or cheque I must have
5096  // written.)
5097  case OTItem::
5098  atAcceptItemReceipt: // <==================================================
5099  {
5100  // What number do I remove here? the user is
5101  // accepting a transfer receipt, which
5102  // is in reference to the recipient's
5103  // acceptPending. THAT item is in reference
5104  // to
5105  // my original transfer (or contains a
5106  // cheque with my original number.) (THAT's
5107  // the # I need.)
5108  //
5109  OTString strOriginalItem;
5110  pServerTransaction->GetReferenceString(
5111  strOriginalItem);
5112 
5113  std::unique_ptr<OTItem> pOriginalItem(
5115  strOriginalItem, SERVER_ID,
5116  pServerTransaction
5117  ->GetReferenceToNum()));
5118 
5119  if (nullptr != pOriginalItem) {
5120  // If pOriginalItem is acceptPending,
5121  // that means I am accepting the
5122  // transfer receipt from the server,
5123  // (from my inbox),
5124  // which has the recipient's acceptance
5125  // inside of my transfer as the original
5126  // item. This means the transfer that
5127  // I originally sent is now finally
5128  // closed!
5129  //
5130  // If it's a depositCheque, that means I
5131  // am accepting the cheque receipt from
5132  // the server, (from my inbox)
5133  // which has the recipient's deposit
5134  // inside of it as the original item.
5135  // This means that the cheque that
5136  // I originally wrote is now finally
5137  // closed!
5138  //
5139  // In both cases, the "original item"
5140  // itself is not from me, but from the
5141  // recipient! Therefore,
5142  // the number on that item is useless
5143  // for removing numbers from my list of
5144  // issued numbers.
5145  // Rather, I need to load that original
5146  // cheque, or pending transfer, from
5147  // WITHIN the original item,
5148  // in order to get THAT number, to
5149  // remove it from my issued list.
5150  //
5151  if (OTItem::depositCheque ==
5152  pOriginalItem
5153  ->GetType()) // I am accepting a
5154  // CHEQUE RECEIPT,
5155  // which has a
5156  // depositCheque
5157  // request (from
5158  // the recipient)
5159  // as the original
5160  // item within.
5161  {
5162  // Get the cheque from the Item and
5163  // load it up into a Cheque object.
5164  OTString strCheque;
5165  pOriginalItem->GetAttachment(
5166  strCheque);
5167 
5168  OTCheque theCheque; // allocated on
5169  // the stack :-)
5170 
5171  if (false ==
5172  ((strCheque.GetLength() > 2) &&
5173  theCheque
5174  .LoadContractFromString(
5175  strCheque))) {
5176  otErr << "ERROR loading cheque "
5177  "from string in "
5178  "OTClient::"
5179  "ProcessServerReply:\n"
5180  << strCheque << "\n";
5181  }
5182  else // Since I wrote the cheque,
5183  // and I am now accepting the
5184  // cheque receipt, I can now
5185  // be cleared
5186  // for that issued number.
5187  // (Because the server reply
5188  // said SUCCESS accepting the
5189  // chequeReceipt/voucherReceipt.)
5190  {
5191  pNym->RemoveIssuedNum(
5192  *pNym, strServerID,
5193  theCheque
5194  .GetTransactionNum(),
5195  true); // bool bSave=true
5196  /* Inside OT, when processing
5197  successful server reply to
5198  processInbox request, if a
5199  chequeReceipt
5200  was processed out successfully
5201  (here: YES), and if that cheque
5202  is found inside the outpayments,
5203  then move it at that time to the
5204  record box. */
5205 
5206  int32_t lOutpaymentsIndex =
5208  *pNym,
5209  theCheque
5210  .GetTransactionNum());
5211 
5212  if (lOutpaymentsIndex >
5213  (-1)) // found something
5214  // that matches...
5215  {
5216  // Remove it from
5217  // Outpayments box. We're
5218  // done with it -- we
5219  // accepted the
5220  // chequeReceipt now.
5221  // (Dump it in records for
5222  // your app, but OT itself
5223  // is done with it.)
5224  //
5225  if (pNym->RemoveOutpaymentsByIndex(
5226  lOutpaymentsIndex)) {
5227  if (!pNym->SaveSignedNymfile(
5228  *pNym)) // <==
5229  // save
5230  // Nym
5231  // to
5232  // local
5233  // storage,
5234  // since
5235  // an
5236  // outpayment
5237  // was
5238  // erased.
5239  otErr
5240  << __FUNCTION__
5241  << ": Error "
5242  "saving "
5243  "Nym: "
5244  << strNymID
5245  << "\n";
5246  }
5247  }
5248  }
5249  }
5250  // I am accepting a TRANSFER RECEIPT,
5251  // which has an acceptPending inside
5252  // FROM THE RECIPIENT,
5253  // as the original item within, (which
5254  // is in reference to my outoing
5255  // original transfer.)
5256  else if (OTItem::acceptPending ==
5257  pOriginalItem->GetType()) {
5258  pNym->RemoveIssuedNum(
5259  *pNym, strServerID,
5260  pOriginalItem
5261  ->GetNumberOfOrigin(),
5262  true); // bool bSave=true
5263  }
5264  else {
5265  OTString strOriginalItemType;
5266  pOriginalItem->GetTypeString(
5267  strOriginalItemType);
5268  otErr << "OTClient::"
5269  "ProcessServerReply: "
5270  "Original item has wrong "
5271  "type, while accepting "
5272  "item receipt:\n"
5273  << strOriginalItemType
5274  << "\n";
5275  }
5276  }
5277  else {
5278  otErr << "OTClient::ProcessServerReply:"
5279  " Unable to load original "
5280  "item from string while "
5281  "accepting item receipt:\n"
5282  << strOriginalItem << "\n";
5283  }
5284  } // OTItem::atAcceptItemReceipt.
5285  break;
5286 
5287  // Cron Receipt: We do not remove the original
5288  // trans# until the Cron job is entirely
5289  // complete. (Many Cron receipts may breeze
5290  // through here before that happens.)
5291  //
5293  // If it's a CRON receipt, find out if it's
5294  // from a MARKET TRADE, and if so,
5295  // add it to my local list of Market Trades,
5296  // for the GUI to use on the market panel.
5297  //
5298  // Todo security: add the actual sale price
5299  // to boths receipts, along with both
5300  // amounts,
5301  // in order to verify the amount moved is in
5302  // keeping with the terms of the original
5303  // offer.
5304  //
5305  OTItem* pServerItem =
5306  pServerTransaction->GetItem(
5307  OTItem::
5308  marketReceipt); // paymentPlan
5309  // and
5310  // smartContract
5311  // are also
5312  // POSSIBLE
5313  // here.
5314 
5315  if (nullptr != pServerItem) {
5316  OTString strOffer, strTrade;
5317  pServerItem->GetAttachment(
5318  strOffer); // contains updated
5319  // offer.
5320  pServerItem->GetNote(
5321  strTrade); // contains updated
5322  // trade.
5323 
5324  OTOffer theOffer;
5325  OTTrade theTrade;
5326 
5327  bool bLoadOfferFromString =
5328  theOffer.LoadContractFromString(
5329  strOffer);
5330  bool bLoadTradeFromString =
5331  theTrade.LoadContractFromString(
5332  strTrade);
5333  if (bLoadOfferFromString &&
5334  bLoadTradeFromString) {
5335  std::unique_ptr<OTDB::TradeDataNym>
5336  pData(dynamic_cast<
5337  OTDB::
5338  TradeDataNym*>(OTDB::CreateObject(
5339  OTDB::
5341  OT_ASSERT(nullptr != pData);
5342 
5343  /*
5344  std::stringstream ss;
5345  ss << theTrade.GetTransactionNum();
5346  pData->transaction_id = ss.str();
5347  ss.str(""); */
5348  pData->transaction_id = to_string<
5349  int64_t>(
5350  theTrade
5351  .GetTransactionNum()); // TransID
5352  // for
5353  // original
5354  // offer.
5355  // (Offer
5356  // may
5357  // trade
5358  // many
5359  // times.)
5360  pData->updated_id = to_string<
5361  int64_t>(
5362  pServerItem
5363  ->GetTransactionNum()); // TransID for BOTH receipts for current trade. (Asset/Currency.)
5364  pData->completed_count =
5365  to_string<int32_t>(
5366  theTrade
5367  .GetCompletedCount());
5368  std::unique_ptr<OTAccount> pAccount(
5370  ACCOUNT_ID, SERVER_ID));
5371 
5372  bool bIsAsset =
5373  (theTrade.GetAssetID() ==
5374  pAccount->GetAssetTypeID());
5375  bool bIsCurrency =
5376  (theTrade.GetCurrencyID() ==
5377  pAccount->GetAssetTypeID());
5378 
5379  if (bIsAsset) {
5380  // pServerItem->GetAmount()
5381  // contains: (lAmountSold); //
5382  // asset
5383 
5384  const OTString strAssetID(
5385  theTrade.GetAssetID());
5386  int64_t lAssetsThisTrade =
5387  pServerItem->GetAmount();
5388  pData->asset_id =
5389  strAssetID.Get();
5390  pData->amount_sold = to_string<
5391  int64_t>(
5392  lAssetsThisTrade); // The
5393  // amount
5394  // of
5395  // ASSETS
5396  // moved,
5397  // this
5398  // trade.
5399  }
5400  else if (bIsCurrency) {
5401  // pServerItem->GetAmount()
5402  // contains: (lTotalPaidOut);
5403  // // currency
5404 
5405  const OTString strCurrencyID(
5406  theTrade.GetCurrencyID());
5407  int64_t lCurrencyThisTrade =
5408  pServerItem->GetAmount();
5409  pData->currency_id =
5410  strCurrencyID.Get();
5411  pData->currency_paid =
5412  to_string<int64_t>(
5413  lCurrencyThisTrade);
5414  }
5415  const time64_t& tProcessDate =
5416  theTrade.GetLastProcessDate();
5417  pData->date = to_string<time64_t>(
5418  tProcessDate);
5419  // The original offer price. (Might
5420  // be 0, if it's a market order.)
5421  //
5422  const int64_t& lPriceLimit =
5423  theOffer.GetPriceLimit();
5424  pData->offer_price =
5425  to_string<int64_t>(lPriceLimit);
5426  const int64_t& lFinishedSoFar =
5427  theOffer.GetFinishedSoFar();
5428  pData->finished_so_far =
5429  to_string<int64_t>(
5430  lFinishedSoFar);
5431  // save to local storage...
5432  //
5433  OTString strUserID(USER_ID);
5434 
5435  std::unique_ptr<OTDB::TradeListNym>
5436  pList;
5437 
5438  if (OTDB::Exists(
5439  OTFolders::Nym().Get(),
5440  "trades", // todo stop
5441  // hardcoding.
5442  strServerID.Get(),
5443  strUserID.Get()))
5444  pList.reset(dynamic_cast<
5447  OTDB::
5449  OTFolders::Nym().Get(),
5450  "trades", // todo stop
5451  // hardcoding.
5452  strServerID.Get(),
5453  strUserID.Get())));
5454  if (nullptr == pList) {
5455  otInfo
5456  << "Creating storage list "
5457  "of trade receipts for "
5458  "Nym: " << strUserID
5459  << "\n";
5460  pList.reset(dynamic_cast<
5461  OTDB::
5462  TradeListNym*>(OTDB::CreateObject(
5463  OTDB::
5464  STORED_OBJ_TRADE_LIST_NYM)));
5465  }
5466  OT_ASSERT(nullptr != pList);
5467  // Loop through and see if we can
5468  // find one that's ALREADY there.
5469  // We can match the asset receipt
5470  // and currency receipt.
5471  // This way we insure there is only
5472  // one in the end, which combines
5473  // info from both.
5474  // This also enables us to calculate
5475  // the sale price!
5476  //
5477  bool bWeFoundIt = false;
5478 
5479  size_t nTradeDataNymCount =
5480  pList->GetTradeDataNymCount();
5481 
5482  for (size_t nym_count = 0;
5483  nym_count < nTradeDataNymCount;
5484  ++nym_count) {
5485  OTDB::TradeDataNym* pTradeData =
5486  pList->GetTradeDataNym(
5487  nym_count);
5488 
5489  if (nullptr ==
5490  pTradeData) // Should never
5491  // happen.
5492  continue;
5493  if (0 ==
5494  pTradeData->updated_id
5495  .compare(
5496  pData
5497  ->updated_id)) // Found
5498  // it!
5499  {
5500  // It's a repeat of the same
5501  // one. (Discard.)
5502  if ((!pTradeData->asset_id
5503  .empty() &&
5504  !pData->asset_id
5505  .empty()) ||
5506  (!pTradeData
5507  ->currency_id
5508  .empty() &&
5509  !pData->currency_id
5510  .empty()))
5511  break;
5512  // Okay looks like one is
5513  // the asset receipt, and
5514  // the other is the currency
5515  // receipt.
5516  // Therefore let's combine
5517  // them into pTradeData!
5518  //
5519  if (pTradeData->asset_id
5520  .empty()) {
5521  pTradeData->asset_id =
5522  pData->asset_id;
5523  pTradeData
5524  ->amount_sold =
5525  pData->amount_sold;
5526  }
5527  else if (pTradeData
5528  ->currency_id
5529  .empty()) {
5530  pTradeData
5531  ->currency_id =
5532  pData->currency_id;
5533  pTradeData
5534  ->currency_paid =
5535  pData
5536  ->currency_paid;
5537  }
5538  if (!pTradeData->amount_sold
5539  .empty() &&
5540  !pTradeData
5541  ->currency_paid
5542  .empty()) {
5543  const int64_t lAmountSold =
5545  pTradeData
5546  ->amount_sold);
5547  const int64_t lCurrencyPaid =
5549  pTradeData
5550  ->currency_paid);
5551 
5552  if (lAmountSold !=
5553  0) // just in case
5554  // (divide by 0.)
5555  {
5556  const int64_t
5557  lSalePrice =
5558  (lCurrencyPaid /
5559  lAmountSold);
5560 
5561  OTString
5562  strSalePrice;
5563  strSalePrice.Format(
5564  "%" PRId64 "",
5565  lSalePrice);
5566 
5567  pTradeData->price =
5568  strSalePrice
5569  .Get();
5570  }
5571  }
5572 
5573  bWeFoundIt = true;
5574 
5575  break;
5576 
5577  } // if we found it.
5578  } // for
5579  if (!bWeFoundIt) // We didn't find
5580  // it. So let's add
5581  // it.
5582  {
5583  pList->AddTradeDataNym(*pData);
5584  }
5585  if (false ==
5587  *pList,
5588  OTFolders::Nym().Get(),
5589  "trades", // todo stop
5590  // hardcoding.
5591  strServerID.Get(),
5592  strUserID.Get()))
5593  otErr
5594  << "OTClient::"
5595  << __FUNCTION__
5596  << ": Failed storing list "
5597  "of trades for Nym. "
5598  "Server ID: "
5599  << strServerID
5600  << " Nym ID: " << strUserID
5601  << " \n";
5602  }
5603  }
5604  // else
5605  // otErr <<
5606  // "OTClient::ProcessServerReply:
5607  // "
5608  // "Expected marketReceipt
5609  // item in transaction in inbox.");
5610  } // OTItem::atAcceptCronReceipt
5611  break;
5612 
5614  otWarn
5615  << "OTClient::ProcessServerReply: "
5616  "Successfully removed finalReceipt "
5617  "with closing num: "
5618  << pServerTransaction->GetClosingNum()
5619  << "\n";
5620  pNym->RemoveIssuedNum(
5621  *pNym, strServerID,
5622  pServerTransaction->GetClosingNum(),
5623  true); // bool bSave=true
5624 
5625  // This should have already been done by
5626  // this point, but I'm putting it here just
5627  // in case,
5628  // while debugging:
5629  //
5630  if (pNym->RemoveIssuedNum(
5631  *pNym, strServerID,
5632  pServerTransaction
5633  ->GetReferenceToNum(),
5634  true)) // bool bSave=true
5635  otWarn << "**** Due to finding a "
5636  "finalReceipt, REMOVING "
5637  "OPENING NUMBER FROM NYM: "
5638  << pServerTransaction
5639  ->GetReferenceToNum()
5640  << " \n";
5641  else
5642  otWarn
5643  << "**** Noticed a finalReceipt, "
5644  "but Opening Number "
5645  << pServerTransaction
5646  ->GetReferenceToNum()
5647  << " had ALREADY been removed from "
5648  "nym. \n";
5649  // The client side keeps a list of active
5650  // (recurring) transactions.
5651  // That is, smart contracts and payment
5652  // plans. I don't think it keeps
5653  // market offers in that list, since we
5654  // already have a list of active
5655  // market offers separately. And market
5656  // offers produce final receipts,
5657  // so basically this piece of code will be
5658  // executed for all final receipts.
5659  // It's not really necessary that it be
5660  // called for market offers, but whatever.
5661  // It is for the others.
5662  //
5664  pServerTransaction->GetReferenceToNum(),
5665  pNym->GetConstID(),
5666  pServerTransaction
5667  ->GetPurportedServerID());
5668  } // OTItem::atAcceptFinalReceipt
5669  break;
5670 
5672  otInfo
5673  << "OTClient::ProcessServerReply: "
5674  "Successfully removed basketReceipt "
5675  "with closing num: "
5676  << pServerTransaction->GetClosingNum()
5677  << "\n";
5678  pNym->RemoveIssuedNum(
5679  *pNym, strServerID,
5680  pServerTransaction->GetClosingNum(),
5681  true); // bool bSave=true
5682  } // OTItem::atAcceptBasketReceipt
5683  break;
5684 
5685  default: // Error
5686  {
5687  bAddToRecordBox = false;
5688  pReplyItem->GetTypeString(
5689  strTempTypeString);
5690  otErr << "OTClient::ProcessServerReply: "
5691  "wrong reply item transaction "
5692  "type: " << strTempTypeString
5693  << "\n";
5694  } break;
5695  } // switch replyItem type
5696  if (bAddToRecordBox) {
5697  if (!bLoadedRecordBox) // We haven't loaded
5698  // / created it yet.
5699  {
5700  bLoadedRecordBox =
5701  (bRecordBoxExists &&
5702  theRecordBox.LoadRecordBox());
5703  if (bRecordBoxExists &&
5704  bLoadedRecordBox)
5705  bLoadedRecordBox =
5706  (theRecordBox
5707  .VerifyContractID() &&
5708  theRecordBox.VerifySignature(
5709  *pNym));
5710  // bLoadedRecordBox
5711  // =
5712  // (theRecordBox.VerifyAccount(*pNym));
5713  // // (No need to load all the Box
5714  // Receipts using VerifyAccount)
5715  else if (!bLoadedRecordBox)
5716  bLoadedRecordBox =
5717  theRecordBox.GenerateLedger(
5718  ACCOUNT_ID, SERVER_ID,
5720  true); // bGenerateFile=true
5721  // by this point, the box DEFINITELY
5722  // exists -- or not. (generation might
5723  // have failed, or verification.)
5724  //
5725  if (!bLoadedRecordBox) {
5726  otOut
5727  << __FUNCTION__
5728  << ": while processing server "
5729  "reply to processInbox: "
5730  "WARNING: Unable to load, "
5731  "verify, or generate "
5732  "recordBox, with IDs: "
5733  << strNymID << " / "
5734  << theReply.m_strAcctID << "\n";
5735  }
5736  }
5737  if (bLoadedRecordBox) {
5738  const OTString strServerTransaction(
5739  *pServerTransaction);
5740  OTTransaction* pNewTransaction =
5741  nullptr;
5742  std::unique_ptr<OTTransactionType>
5743  pTransType(
5745  TransactionFactory(
5746  strServerTransaction));
5747 
5748  pNewTransaction =
5749  dynamic_cast<OTTransaction*>(
5750  pTransType.get());
5751  if (nullptr != pNewTransaction) {
5752  const bool bAdded =
5753  theRecordBox.AddTransaction(
5754  *pNewTransaction);
5755 
5756  if (!bAdded) {
5757  otErr
5758  << __FUNCTION__
5759  << ": Unable to add "
5760  "transaction "
5761  << pNewTransaction
5762  ->GetTransactionNum()
5763  << " to record box (still "
5764  "removing it from asset "
5765  "account inbox, "
5766  "however.)\n";
5767  }
5768  else // Success adding it to the
5769  // record box (let's save
5770  // it.)
5771  {
5772  // If successfully added to the
5773  // record box, then no need
5774  // anymore to clean it up
5775  // ourselves. The record box
5776  // owns it now.
5777  pTransType.release();
5778 
5779  theRecordBox
5780  .ReleaseSignatures();
5781  theRecordBox.SignContract(
5782  *pNym);
5783  theRecordBox.SaveContract();
5784  theRecordBox
5785  .SaveRecordBox(); // todo
5786  // log
5787  // failure.
5788 
5789  // Any inbox/nymbox/outbox
5790  // ledger will only itself
5791  // contain
5792  // abbreviated versions of the
5793  // receipts, including their
5794  // hashes.
5795  //
5796  // The rest is stored
5797  // separately, in the box
5798  // receipt, which is created
5799  // whenever a receipt is added
5800  // to a box (here), and deleted
5801  // after a receipt
5802  // is removed from a box.
5803  //
5804  if (!pNewTransaction
5805  ->SaveBoxReceipt(
5806  theRecordBox)) // <===================
5807  otErr
5808  << __FUNCTION__
5809  << ": for Record "
5810  "Box... Failed "
5811  "trying to "
5812  "SaveBoxReceipt. "
5813  "Contents:\n\n"
5814  << strServerTransaction
5815  << "\n\n";
5816  }
5817  } // if (nullptr != pNewTransaction)
5818  } // if (bLoadedRecordBox)
5819  } // if (bAddToRecordBox)
5820  // REMOVE IT FROM THE INBOX.
5821  //
5822  // This removal happens for ALL of the above
5823  // cases.
5824  // Update: Now when removing receipts from any
5825  // box, we have to
5826  // also delete the box receipt, which is stored
5827  // as a separate file.
5828  //
5829  pServerTransaction->DeleteBoxReceipt(
5830  theInbox); // faster
5831  // theInbox.DeleteBoxReceipt(pServerTransaction->GetTransactionNum());
5832  theInbox.RemoveTransaction(
5833  pServerTransaction->GetTransactionNum());
5834 
5835  } // for loop (reply items)
5836  // Save the Inbox
5837  //
5838  theInbox.ReleaseSignatures();
5839  theInbox.SignContract(*pNym);
5840  theInbox.SaveContract();
5841  theInbox.SaveInbox();
5842  } // if pReplyTransaction
5843  } // if pTransaction
5844  }
5845  //
5846  else // @processNymbox. // We're processing the SERVER's REPLY
5847  // to our processNymbox request.
5848  {
5849  pTransaction =
5850  theLedger.GetTransaction(OTTransaction::processNymbox);
5851  pReplyTransaction = theReplyLedger.GetTransaction(
5853 
5854  // If I have already processed this reply,
5855 
5856  // We did NOT have to burn a transaction number to process
5857  // the Nymbox, so we don't
5858  // have to remove it from the list of responsibility, like
5859  // we do above.
5860  // The reason is because the Nymbox cannot be used for
5861  // financial transactions, since
5862  // it is associated with a user acct (instead of asset
5863  // account.)
5864  // THIS IS ACTUALLY the WHOLE POINT of the Nymbox: If it
5865  // required a transaction number
5866  // to process the Nymbox, and you use the Nymbox to get
5867  // transaction numbers, then how
5868  // can you ever get a new number if you run out? You need a
5869  // number to get a number?
5870  //
5871  // That makes no logical sense. Therefore, the Nymbox
5872  // provides a way to get new transaction
5873  // numbers WITHOUT HAVING TO BURN ONE TO DO IT. You still
5874  // have to do a transaction statement
5875  // to do it (sign off on the ones that you actually do
5876  // have), but you can still process
5877  // the Nymbox even if you have zero transaction numbers,
5878  // whereas with the inbox for an asset
5879  // account, you cannot process it until you burn a
5880  // transaction number to do so. And if you
5881  // don't have any transaction numbers to do that with,
5882  // that's fine: you just get a new one
5883  // via your nymbox. This is the original reason that I
5884  // added nymboxes in the first place.
5885  //
5886  // SIMILARLY, when a transaction number is REMOVED from our
5887  // list via the Nymbox, it's only
5888  // a NOTIFICATION. The Nymbox cannot actually REMOVE your
5889  // transaction numbers, but it CAN
5890  // be used to drop a notice informing you that one was
5891  // removed. (Usually by a recurring
5892  // transaction, such as a market offer, where you had
5893  // already provided the closing number in
5894  // advance, and you expected that it could be closed at
5895  // anytime.)
5896  //
5897  //
5898  if ((nullptr != pTransaction) &&
5899  (nullptr != pReplyTransaction)) {
5900  // HARVEST TRANSACTION NUMBERS (Nymbox only)
5901  //
5902  OTItem* pStatementItem =
5903  pTransaction->GetItem(OTItem::transactionStatement);
5904 
5905  // We found it!
5906  if (nullptr == pStatementItem) {
5907  otOut
5908  << "Strange... found transaction in ledger in "
5909  << theReply.m_strCommand
5910  << ", but didn't find a transactionStatement "
5911  "item within.\n";
5912  }
5913  else if (!pReplyTransaction->GetSuccess()) {
5914  otOut << "Found the receipt you're talking about, "
5915  "in ledger in " << theReply.m_strCommand
5916  << ", but the Server's Reply transaction "
5917  "says FAILED.\n";
5918  }
5919  else {
5920  OTString strMessageNym;
5921  OTPseudonym theMessageNym;
5922 
5923  pStatementItem->GetAttachment(strMessageNym);
5924 
5925  if (strMessageNym.Exists() &&
5926  theMessageNym.LoadFromString(strMessageNym)) {
5927  // Success!
5928  // Whatever Trans#'s I accepted when I processed
5929  // my nymbox, I now
5930  // harvest them onto my Nym for use. (Couldn't
5931  // be sure until server replied "success".)
5932  //
5933  // Contrast this with the numbers removed. In
5934  // the case of Nymbox, I cannot
5935  // remove numbers, only receive notice that a
5936  // number was already removed.
5937  // Therefore, I might as well remove it on my
5938  // side also, as soon as I see that
5939  // notice (and approve of it.) There's no need
5940  // juggling it in that case -- it's
5941  // already gone. (Therefore it's already been
5942  // done by the time we're in this
5943  // function reading the server's reply. Removals
5944  // for Nymbox happen in Finalize for
5945  // processNymbox, and in AcceptEntireNymbox.)
5946  // Below however, are additions, not removals,
5947  // so we don't add them until the
5948  // server has DEFINITELY responded in the
5949  // affirmative (here):
5950  //
5952  pStatementItem->GetPurportedServerID(),
5953  *pNym, theMessageNym, true); // bSave=true
5954  // New version now takes tentative numbers into
5955  // account, to reduce sync issues.
5956  // pNym->HarvestIssuedNumbers(pStatementItem->GetPurportedServerID(),
5957  // *pNym,
5958  // theMessageNym, true); // bSave=true
5959  }
5960  else {
5961  otOut << "Strange... found transaction item in "
5962  "ledger in " << theReply.m_strCommand
5963  << ", but didn't find theMessageNym "
5964  "within.\n";
5965  }
5966  }
5967 
5968  //
5969  // REMOVE VARIOUS ITEMS FROM THE LOCAL NYMBOX (THEIR
5970  // TIME IS DONE.)
5971 
5972  // Load the Nymbox.
5973  OTLedger theNymbox(USER_ID, USER_ID, SERVER_ID);
5974  bool bLoadedNymbox = false;
5975  if (nullptr !=
5976  pNymbox) // If a pointer was passed in, then
5977  // we'll just use it.
5978  {
5979  bLoadedNymbox = true;
5980  }
5981  else // Otherwise, we have to load it ourselves. (And
5982  // point the pointer to it.)
5983  {
5984  pNymbox = &theNymbox;
5985  bLoadedNymbox = (pNymbox->LoadNymbox() &&
5986  pNymbox->VerifyAccount(*pNym));
5987  }
5988  // I JUST had this loaded if I sent acceptWhatever just
5989  // instants ago, (which I am now processing the reply
5990  // for.)
5991  // Therefore I'm just ASSUMING here that it loads
5992  // successfully here, since it worked an instant ago.
5993  // Todo.
5994  //
5995  OT_ASSERT_MSG(bLoadedNymbox,
5996  "Was trying to load Nymbox.");
5997  // Next, loop through the reply items for each "process
5998  // nymbox" item that I must have previously sent.
5999  // For each, if successful, remove from inbox.
6000  // For item receipts, if successful, also remove the
6001  // appropriate trans#
6002  // from my issued list of transaction numbers (like
6003  // above.)
6004  //
6005  for (auto& it : pReplyTransaction->GetItemList()) {
6006  OTItem* pReplyItem = it;
6007  OT_ASSERT_MSG(nullptr != pReplyItem,
6008  "OTClient::ProcessServerReply: "
6009  "Pointer should not have been "
6010  "nullptr.");
6011 
6012  OTItem::itemType theItemType = OTItem::error_state;
6013 
6014  switch (pReplyItem->GetType()) {
6015  case OTItem::atAcceptFinalReceipt: // for inbox this
6016  // is a closing
6017  // issued number
6018  // being removed from your list.
6019  theItemType =
6020  OTItem::acceptFinalReceipt; // but for
6021  // Nymbox, this
6022  // is only a
6023  // notification
6024  // that it
6025  // already
6026  // happened
6027  // previously.
6028  break;
6030  theItemType = OTItem::acceptMessage;
6031  break;
6033  theItemType = OTItem::acceptNotice;
6034  break;
6036  theItemType = OTItem::acceptTransaction;
6037  break;
6038  // FYI, on server side, it does not bother to
6039  // process an item,
6040  // if the balance statement or transaction statement
6041  // has not succeeded.
6042  //
6043  // Thus, if the ITEM ITSELF has succeeded, that
6044  // means the balance or
6045  // transaction statement MUST have succeeded!
6046  // Because server wouldn't have
6047  // even bothered to process the item otherwise.
6048  //
6049  // There still might be some future application in
6050  // doing something with these
6051  // statements when they come in.
6053  theItemType =
6054  OTItem::transactionStatement; // We just
6055  // continue;
6056  // when this
6057  // happens,
6058  // and skip
6059  // this one.
6060  continue; // (The transaction statement itself
6061  // is already handled before this
6062  // "for" loop.)
6063 
6064  default: {
6065  OTString strTempTypeString;
6066  pReplyItem->GetTypeString(strTempTypeString);
6067  otErr << __FUNCTION__
6068  << ": Unexpected replyItem:type while "
6069  "processing Nymbox: "
6070  << strTempTypeString << " \n";
6071  continue;
6072  }
6073  } // SWITCH
6074  // The below actions are only necessary if
6075  // pReplyItem was a SUCCESS.
6076  // (Otherwise we skip them...)
6077  //
6078  OTString strTempTypeString;
6079  pReplyItem->GetTypeString(strTempTypeString);
6080 
6082  pReplyItem->GetStatus()) {
6083  otWarn << "@processNymbox reply item "
6084  << strTempTypeString
6085  << ": status == FAILED\n";
6086  continue;
6087  }
6088  // else
6089  otWarn << "@processNymbox reply item "
6090  << strTempTypeString
6091  << ": status == SUCCESS\n";
6092 
6093  // pReplyItem->GetReferenceToNum() contains the
6094  // process transaction# of pItem (0, in
6095  // a transaction statement, since it usually has no
6096  // transaction number of its own), not
6097  // the inbox receipt # that pItem is in reference
6098  // to.
6099  //
6100  // pTransaction is the processNymbox transaction
6101  // request that I sent.
6102  // (The items within it all share its same
6103  // transaction number, but they are IN REFERENCE TO
6104  // the Nymbox receipts that they accept/reject.)
6105  // pReplyTransaction is the server's reply to that.
6106  // pReplyItem is the current item when iterating
6107  // through pReplyTransaction.
6108  // pItem is the corresponding REQUEST item from
6109  // pTransaction, that pReplyItem is responding to.
6110  //
6111  // Therefore: I need to load the original item from
6112  // pReplyItem's reference string (it's bundled in
6113  // there).
6114  // THEN I will get the "in reference to" number from
6115  // THAT (which is the nymbox Receipt #).
6116  // THEN I will use that number to look up the SAME
6117  // original item from pTransaction.
6118  // The last step isn't technically necessary, but
6119  // may be useful for security.
6120  //
6121  // Sheesh!
6122 
6123  OTString strProcessNymboxItem;
6124  pReplyItem->GetReferenceString(
6125  strProcessNymboxItem);
6126 
6127  std::unique_ptr<OTItem> pProcessNymboxItem(
6129  strProcessNymboxItem, SERVER_ID,
6130  0 /* 0 is the "transaction number"*/)); // todo
6131  // stop
6132  // hardcoding.
6133 
6134  // pProcessNymboxItem is already a copy of the
6135  // correct processNymbox item that I need.
6136  // But still, it's a copy that the SERVER sent me.
6137  // So I'm going to use it to get the
6138  // reference number that I need, in order to look up
6139  // MY copy of the item.
6140  //
6141  OTItem* pItem = (pProcessNymboxItem != nullptr)
6142  ? pTransaction->GetItemInRefTo(
6143  pProcessNymboxItem
6144  ->GetReferenceToNum())
6145  : nullptr;
6146 
6147  if (nullptr == pItem) {
6148  otErr << __FUNCTION__
6149  << ": Unable to find original item in "
6150  "original processNymbox transaction "
6151  "request, based on reply item.\n";
6152  continue;
6153  }
6154  // If this happens, it means the item we found in
6155  // our original process Nymbox transaction, which
6156  // matched the
6157  // "in reference to" number that we expected from
6158  // the copy of that original item we loaded from
6159  // within the
6160  // pReplyItem that's supposedly responding to it,
6161  // does not have the same TYPE that we would have
6162  // expected it to
6163  // have, based on the intelligence in the above
6164  // switch statement.
6165  //
6166  if (pItem->GetType() !=
6167  theItemType) { // (Possible types for pItem:
6168  // acceptMessage, acceptNotice,
6169  // acceptTransactions,
6170  // acceptFinalReceipt.)
6171  otErr << __FUNCTION__
6172  << ": Wrong original item TYPE, on reply "
6173  "item's copy of original item, than "
6174  "what was expected based on reply "
6175  "item's type.\n";
6176  continue;
6177  }
6178 
6179  // Todo here: any other verification of pItem
6180  // against pProcessNymboxItem, which are supposedly
6181  // copies of the same item.
6182  // (Potentially todo security.)
6183 
6184  // FYI, pItem->GetReferenceToNum() is the ID of the
6185  // receipt that's in the Nymbox.
6186  //
6187  OTTransaction* pServerTransaction = nullptr;
6188 
6189  otWarn << __FUNCTION__
6190  << ": Checking client-side Nymbox for "
6191  "expected Nymbox item: "
6192  << pItem->GetReferenceToNum()
6193  << "... \n"; // temp remove
6194 
6195  switch (pReplyItem->GetType()) {
6200  pServerTransaction = pNymbox->GetTransaction(
6201  pItem->GetReferenceToNum());
6202  break;
6203 
6204  default: {
6205  OTString strTempTypeString;
6206  pReplyItem->GetTypeString(strTempTypeString);
6207  otErr << __FUNCTION__
6208  << ": Unexpected replyItem::type while "
6209  "processing Nymbox: "
6210  << strTempTypeString << " \n";
6211  break;
6212  }
6213  }
6214  if (nullptr == pServerTransaction) {
6215  otWarn << __FUNCTION__
6216  << ": The original processNymbox item "
6217  "referred to trans number "
6218  << pItem->GetReferenceToNum()
6219  << ", but that receipt wasn't in my "
6220  "Nymbox. (We probably processed this "
6221  "server reply ALREADY, and now we're "
6222  "just seeing it again, since an "
6223  "extra copy was dropped into the "
6224  "Nymbox originally. It happens. "
6225  "Skipping.)";
6226  break; // We must have processed this reply
6227  // already, and it just came through
6228  // again cause a copy was in a nymbox
6229  // notice.
6230  }
6231  // All of these need to remove something from the
6232  // client-side Nymbox. (Which happens below this
6233  // switch.)
6234  //
6235  switch (
6236  pReplyItem->GetType()) { // Some also need to
6237  // remove an issued
6238  // transaction number
6239  // from pNym.
6240 
6242 
6243  // There are many different types of notices.
6244  // We just indiscriminately accept them all from
6245  // the Nymbox.
6246  // The replyNotice tells you that a transaction
6247  // was processed.
6248  // (We put a copy of the server reply into your
6249  // Nymbox, to make sure
6250  // you get it, so you stay in sync with which
6251  // transaction numbers are signed out.)
6252  // The successNotice tells you that you
6253  // successfully signed out new transaction
6254  // numbers
6255  // (to use on transactions.)
6256  // The "plain-ole" OTTransaction::notice is used
6257  // to notice the parties to a smart
6258  // contract that it has activated (or failed to
6259  // activate.)
6260 
6261  // if pReplyItem is atAcceptNotice, then pItem
6262  // is acceptNotice.
6263  // Then pItem is accepting (IN REFERENCE TO) the
6264  // original OTItem::notice that's
6265  // sitting in the Nymbox!
6266 
6267  if (OTTransaction::notice ==
6268  pServerTransaction->GetType()) {
6269 
6270  if ((OTItem::rejection ==
6271  pReplyItem
6272  ->GetStatus()) || // REJECTION
6274  pReplyItem
6275  ->GetStatus())) // ACKNOWLEDGMENT
6276  {
6277  // NOTE: NORMALLY we do this sort of
6278  // thing in the server reply to the
6279  // actual
6280  // transaction request (by the
6281  // activating party.)
6282  //
6283  // For example, if you tried to activate
6284  // a smart contract, and that failed,
6285  // then
6286  // the atSmartContract server reply will
6287  // be processed, and the opening issued#
6288  // will
6289  // be removed at that time, and the
6290  // closing numbers will be harvested. So
6291  // then, why
6292  // this additional notice in my Nymbox?
6293  // If that will already happen?
6294  //
6295  // ===> Because of ALL THE OTHER PARTIES
6296  // to the smart contract! (This may be
6297  // necessary
6298  // for payment plans, too.) The
6299  // activating party got his reply (he
6300  // even had a back-up
6301  // reply stuffed into his Nymbox to make
6302  // SURE he got it.) But all the other
6303  // parties will
6304  // only know, if they are sent a notice!
6305  // Therefore a notice is sent by the
6306  // server, to all
6307  // parties.
6308  //
6309  // ===> This also means that the
6310  // ACTIVATING party himself will ALSO
6311  // get this same notice!
6312  // But since we've already established
6313  // above that the activating party
6314  // ALREADY processes
6315  // his activation reply, we don't want
6316  // him to process it TWICE!
6317  //
6318  // Therefore, we will process the notice
6319  // like normal, UNLESS pNym is the
6320  // activating Nym for
6321  // the smart contract, in which case we
6322  // skip it, since we assume he already
6323  // processed the
6324  // reply directly when he activated the
6325  // smart contract.
6326  //
6327  // You might ask, then why not just let
6328  // the activating party, process this
6329  // notice here the
6330  // same as all the other parties, and
6331  // just NOT have him process it on the
6332  // direct reply, as
6333  // he is now? The answer is, because he
6334  // will stay in sync better if we just
6335  // give him that
6336  // info as soon as he's able to receive
6337  // it, which is preferably RIGHT when he
6338  // performs the
6339  // activation. The other parties are not
6340  // currently present, so they HAVE to be
6341  // informed by
6342  // notices. But the ACTIVATING party
6343  // might as well be informed instantly.
6344  // Otherwise he will
6345  // just be out of sync until the next
6346  // time he processes his Nymbox, which
6347  // causes unnecessary
6348  // delays as it will result in
6349  // unnecessary server messages to resync
6350  // the situation.
6351  //
6352  // THEREFORE: We will skip this step if
6353  // pNym is the activating Nym, since
6354  // he's assumed to
6355  // have done this already. Otherwise,
6356  // pNym is NOT the activating Nym, and
6357  // he's one of the
6358  // other parties receiving this notice,
6359  // and therefore he needs to process it
6360  // accordingly
6361  // (He, in fact, processes it here
6362  // IDENTICALLY as the activating Nym
6363  // does when he receives
6364  // the reply to his transaction request:
6365  // by removing the issued opening
6366  // number, and by
6367  // harvesting the closing numbers.)
6368  // If it was a failure, harvest the
6369  // extra transaction numbers that were
6370  // used as
6371  // CLOSING numbers. They can go back on
6372  // my Nym and be used another day!
6373  // Remove
6374  // the opening number and harvest the
6375  // closing ones, basically.
6376  //
6377 
6378  OTString strCronItem;
6379  pServerTransaction->GetReferenceString(
6380  strCronItem);
6381 
6382  // What kind of cron item is it?
6383  // Well (todo) we should probably
6384  // double-check, but the only cron items
6385  // we
6386  // send notices for are payment plans
6387  // and smart contracts. Market offers
6388  // don't
6389  // need notices, since anyone activating
6390  // a market offer is already getting the
6391  // reply. (AND getting a copy of that
6392  // reply, already, inside a replyNotice
6393  // in
6394  // his Nymbox...) So he can't possibly
6395  // miss the server's reply, and there
6396  // aren't
6397  // any other parties to notify (re:
6398  // successful activation), besides the
6399  // Nym himself.
6400  //
6401  // Only payment plans and smart
6402  // contracts could potentially have some
6403  // other signer, who
6404  // would want to get notified, and to
6405  // whom the notice is send.
6406  //
6407  std::unique_ptr<OTCronItem> pCronItem(
6408  (strCronItem.Exists()
6410  strCronItem)
6411  : nullptr));
6412 
6413  if (nullptr !=
6414  pCronItem) // the original
6415  // smart contract
6416  // or payment
6417  // plan object.
6418  {
6419  OTIdentifier theCancelerNymID;
6420  const int64_t lNymOpeningNumber =
6421  pCronItem->GetOpeningNumber(
6422  pNym->GetConstID());
6423  const bool bCancelling =
6424  (pCronItem->IsCanceled() &&
6425  pCronItem->GetCancelerID(
6426  theCancelerNymID));
6427  const bool bIsCancelerNym =
6428  (bCancelling &&
6429  (pNym->GetConstID() ==
6430  theCancelerNymID));
6431  const bool bIsActivatingNym =
6432  (pCronItem->GetOpeningNum() ==
6433  lNymOpeningNumber); // If the
6434  // opening
6435  // number
6436  // for the
6437  // cron
6438  // item is
6439  // the SAME
6440  // as Nym's
6441  // opening
6442  // number,
6443  // then Nym
6444  // is the
6445  // ACTIVATING
6446  // NYM
6447  // (Skip
6448  // him,
6449  // since he
6450  // does
6451  // this
6452  // same
6453  // stuff
6454  // when he
6455  // receives
6456  // the
6457  // actual
6458  // server
6459  // reply.
6460  // The
6461  // notices
6462  // are for
6463  // the
6464  // OTHER
6465  // parties)...
6466 
6467  // Canceler (if cancelling) or
6468  // activator (if activating) are
6469  // handled already elsewhere, when
6470  // they receive
6471  // the server reply. A notice is
6472  // also sent to all the parties (and
6473  // we're processing that notice now)
6474  // so here
6475  // we just need to handle everyone
6476  // else but him.
6477  //
6478  if ((bCancelling &&
6479  !bIsCancelerNym) || // If
6480  // canceling,
6481  // and Nym
6482  // is not
6483  // the
6484  // canceler...
6485  (!bCancelling &&
6486  !bIsActivatingNym) // or if
6487  // activating,
6488  // and Nym
6489  // is not
6490  // the
6491  // activator...
6492  ) {
6493  if (OTItem::rejection ==
6494  pReplyItem
6495  ->GetStatus()) // REJECTION
6496  // (This
6497  // is
6498  // where
6499  // we
6500  // remove
6501  // the
6502  // opening
6503  // number,
6504  // and
6505  // harvest
6506  // the
6507  // closing
6508  // numbers.)
6509  {
6510  // Why do this? Oh I see,
6511  // this number either gets
6512  // burned from the attempt,
6513  // or it stays open for a
6514  // while if success. So here
6515  // what do we see? The
6516  // rejection
6517  // burning the transaction
6518  // number, but leaving it
6519  // open if success. Perfect.
6520  //
6521  if (false ==
6522  pNym->RemoveIssuedNum(
6523  *pNym, strServerID,
6524  lNymOpeningNumber,
6525  true)) // bool
6526  // bSave=true
6527  {
6528  otErr
6529  << __FUNCTION__
6530  << ": Error "
6531  "removing "
6532  "issued number "
6533  "from user nym "
6534  "(for a cron "
6535  "item.)\n";
6536  }
6537  // If the activation was a
6538  // failure, we can add all
6539  // the extra transaction
6540  // numbers BACK to the
6541  // Nym, that were being used
6542  // as CLOSING numbers, and
6543  // use them later. (They
6544  // aren't burned.)
6545  // They're still all
6546  // signed-out, so we should
6547  // harvest them so we can
6548  // still use them on
6549  // something.
6550  // (Whereas if it had been a
6551  // success, then we would
6552  // have left them in their
6553  // existing state, since
6554  // the transaction would
6555  // then be in play, and the
6556  // numbers could not be used
6557  // again, nor removed as
6558  // issued numbers until the
6559  // transaction itself had
6560  // finished and its receipts
6561  // had been signed-off.)
6562  //
6563  pCronItem
6564  ->HarvestClosingNumbers(
6565  *pNym); // saves.
6566  }
6567  // If success, save a copy in my
6568  // "active cron items" folder.
6569  //
6570  else // if (OTItem::acknowledged
6571  // ==
6572  // pReplyItem->GetStatus())
6573  {
6574  pCronItem
6575  ->SaveActiveCronReceipt(
6576  pNym->GetConstID());
6577  }
6578  // When party receives notice
6579  // that smart contract has been
6580  // activated,
6581  // remove the instrument from
6582  // outpayments box. (If it's
6583  // there -- it can be.)
6584  //
6585  // (This happens for
6586  // acknowledged AND rejected
6587  // smart contracts.)
6588  //
6589  OTNumList numlistOutpayment(
6590  lNymOpeningNumber);
6591  OTString strInstrument; // If
6592  // the
6593  // instrument is
6594  // in the
6595  // outpayments
6596  // box, we put a
6597  // copy of it
6598  // here.
6599  const int32_t nOutpaymentIndex =
6601  *pNym,
6602  lNymOpeningNumber);
6603  std::unique_ptr<OTMessage>
6604  theMessageAngel;
6605 
6606  if (nOutpaymentIndex >= 0) {
6607  OTMessage* pMsg =
6608  pNym->GetOutpaymentsByIndex(
6609  nOutpaymentIndex);
6610 
6611  if (nullptr == pMsg) {
6612  otErr
6613  << __FUNCTION__
6614  << ": Unable to "
6615  "find payment "
6616  "message in "
6617  "outpayment box "
6618  "based on index "
6619  << nOutpaymentIndex
6620  << ".\n";
6621  }
6622  else {
6623  const bool bRemovedOutpayment =
6625  nOutpaymentIndex,
6626  false); // bDeleteIt=false
6627  // (deleted
6628  // later
6629  // on.)
6630  theMessageAngel.reset(
6631  pMsg);
6632  // Since we
6633  // chose to
6634  // keep pMsg
6635  // alive and
6636  // undeleted,
6637  // after
6638  // removing
6639  // it from
6640  // the
6641  // outpayments
6642  // box, we
6643  // set the
6644  // angel here
6645  // to make
6646  // sure it
6647  // gets
6648  // cleaned up
6649  // later,
6650  // whenever
6651  // we return
6652  // out of
6653  // this
6654  // godforsaken
6655  // function.
6656  if (bRemovedOutpayment)
6657  pNym->SaveSignedNymfile(
6658  *pNym);
6659  else
6660  otErr
6661  << __FUNCTION__
6662  << ": Failed "
6663  "trying to "
6664  "remove "
6665  "outpayment "
6666  "at index: "
6667  << nOutpaymentIndex
6668  << "\n";
6669  if (!pMsg->m_ascPayload
6670  .GetString(
6671  strInstrument)) {
6672  otErr
6673  << __FUNCTION__
6674  << ": Unable "
6675  "to find "
6676  "payment "
6677  "instrument "
6678  "in "
6679  "outpayment "
6680  "message at "
6681  "index "
6682  << nOutpaymentIndex
6683  << ".\n";
6684  }
6685  else {
6686  // At this point,
6687  // we've removed the
6688  // outpayment
6689  // already, and it
6690  // will be deleted
6691  // when it goes out
6692  // of scope already.
6693  // And we've got a
6694  // copy of the
6695  // original
6696  // financial
6697  // instrument that
6698  // was SENT in that
6699  // outpayment.
6700  //
6701  // But what for? Why
6702  // did I want that
6703  // instrument here
6704  // in a string, in
6705  // strInstrument?
6706  // Do I still need
6707  // to do something
6708  // with it? Yes: I
6709  // need to drop a
6710  // copy of it into
6711  // the record box!
6712 
6713  // NOTE:
6714  // strInstrument is
6715  // added to the
6716  // RecordBox below.
6717  // So there's no
6718  // need to
6719  // do that here,
6720  // ATM.
6721  }
6722  }
6723  }
6724  // When party receives notice
6725  // that smart contract has
6726  // failed activation attempt,
6727  // then remove
6728  // the instrument from payments
6729  // inbox AND outpayments box.
6730  // (If there -- could be for
6731  // either.)
6732  // (Outbox is done just above,
6733  // so now let's do inbox...)
6734  //
6735 
6736  // Why only rejected items? Why
6737  // not remove it from the
6738  // payments inbox on success as
6739  // well?
6740  // Normally wouldn't we expect
6741  // that a successful activation
6742  // of an inbox item, should
6743  // remove
6744  // that inbox item? Especially
6745  // if there's already a copy in
6746  // the outbox as well...
6747  //
6748  // if
6749  // (OTItem::rejection ==
6750  // pReplyItem->GetStatus()) //
6751  // REJECTION
6752  {
6753  const bool bExists1 =
6754  OTDB::Exists(
6755  OTFolders::
6756  PaymentInbox()
6757  .Get(),
6758  strServerID.Get(),
6759  strNymID.Get());
6760  const bool bExists2 =
6761  OTDB::Exists(
6762  OTFolders::
6763  RecordBox()
6764  .Get(),
6765  strServerID.Get(),
6766  strNymID.Get());
6767  OTLedger thePmntInbox(
6768  USER_ID, USER_ID,
6769  SERVER_ID); // payment
6770  // inbox
6771  OTLedger theRecordBox(
6772  USER_ID, USER_ID,
6773  SERVER_ID); // record
6774  // box
6775  bool bSuccessLoading1 =
6776  (bExists1 &&
6777  thePmntInbox
6778  .LoadPaymentInbox());
6779  bool bSuccessLoading2 =
6780  (bExists2 &&
6781  theRecordBox
6782  .LoadRecordBox());
6783  if (bExists1 &&
6784  bSuccessLoading1)
6785  bSuccessLoading1 =
6786  (thePmntInbox
6787  .VerifyContractID() &&
6788  thePmntInbox
6789  .VerifySignature(
6790  *pNym));
6791  // bSuccessLoading1
6792  // =
6793  // (thePmntInbox.VerifyAccount(*pNym));
6794  // // (No need to load all
6795  // the Box Receipts using
6796  // VerifyAccount)
6797  else if (!bExists1)
6798  bSuccessLoading1 =
6799  thePmntInbox
6800  .GenerateLedger(
6801  USER_ID,
6802  SERVER_ID,
6803  OTLedger::
6804  paymentInbox,
6805  true); // bGenerateFile=true
6806  if (bExists2 &&
6807  bSuccessLoading2)
6808  bSuccessLoading2 =
6809  (theRecordBox
6810  .VerifyContractID() &&
6811  theRecordBox
6812  .VerifySignature(
6813  *pNym));
6814  // bSuccessLoading2
6815  // =
6816  // (theRecordBox.VerifyAccount(*pNym));
6817  // // (No need to load all
6818  // the Box Receipts using
6819  // VerifyAccount)
6820  else if (!bExists2)
6821  bSuccessLoading2 =
6822  theRecordBox
6823  .GenerateLedger(
6824  USER_ID,
6825  SERVER_ID,
6826  OTLedger::
6827  recordBox,
6828  true); // bGenerateFile=true
6829  // by this point, the boxes
6830  // DEFINITELY exist -- or
6831  // not. (generation might
6832  // have failed, or
6833  // verification.)
6834  //
6835  if (!bSuccessLoading1 ||
6836  !bSuccessLoading2) {
6837  otOut
6838  << __FUNCTION__
6839  << ": while "
6840  "processing "
6841  "server "
6842  "rejection of "
6843  "cron item: "
6844  "WARNING: "
6845  "Unable to "
6846  "load, verify, "
6847  "or generate "
6848  "paymentInbox "
6849  "or recordBox, "
6850  "with IDs: "
6851  << strNymID << " / "
6852  << strNymID << "\n";
6853  }
6854  else // --- ELSE ---
6855  // Success loading
6856  // the payment inbox
6857  // and recordBox and
6858  // verifying
6859  { // their contractID and
6860  // signature, (OR
6861  // success
6862  // generating the
6863  // ledger.)
6864  // See if there's a
6865  // receipt in the
6866  // payments inbox.
6867  // If so, remove it.
6868  //
6869  // What's going on here?
6870  //
6871  // Well let's say Alice
6872  // sends Bob a payment
6873  // plan. (This applies
6874  // to smart contracts,
6875  // too.)
6876  // This means Bob has a
6877  // payment plan in his
6878  // PAYMENTS INBOX, with
6879  // the recipient's
6880  // (Alice)
6881  // transaction number
6882  // set to X, and the
6883  // sender's transaction
6884  // number set to 0. It's
6885  // 0 because
6886  // the instrument is
6887  // still in Bob's inbox
6888  // -- he hasn't signed
6889  // it yet -- so his
6890  // transaction
6891  // number isn't on it
6892  // yet. It's blank (0).
6893  //
6894  // Next, let's say Bob
6895  // signs/confirms the
6896  // contract, which puts
6897  // a copy of it into his
6898  // PAYMENTS
6899  // OUTBOX. On the outbox
6900  // version, Alice's
6901  // transaction number is
6902  // X, and Bob's
6903  // transaction number
6904  // is Y.
6905  //
6906  // Later on, Bob needs
6907  // to lookup the payment
6908  // plan in his PAYMENTS
6909  // INBOX (for example,
6910  // to remove
6911  // it, AS YOU SEE IN THE
6912  // BELOW LOOP.)
6913  // Remember, Bob's
6914  // transaction number is
6915  // Y. But he can't use
6916  // that number (Y) to
6917  // lookup the payment
6918  // plan in his inbox,
6919  // since it's set to
6920  // ZERO in his inbox!
6921  // The inbox version
6922  // simply doesn't HAVE Y
6923  // set onto it yet --
6924  // only the outbox
6925  // version does.
6926  //
6927  // So how in the fuck
6928  // does Bob lookup the
6929  // inbox version, if the
6930  // transaction number
6931  // isn't SET on
6932  // it yet??
6933  //
6934  // The solution:
6935  // 1. Bob grabs an
6936  // OTNumList containing
6937  // all the transaction
6938  // numbers from the
6939  // OUTBOX VERSION,
6940  // which ends up
6941  // containing "X,Y"
6942  // (that happens in this
6943  // block.)
6944  // 2. Bob loops through
6945  // the payments INBOX,
6946  // and for each, he
6947  // grabs an OTNumList
6948  // containing all
6949  // the transaction
6950  // numbers. One of those
6951  // (the matching one)
6952  // will contain "X,0".
6953  // (Except it
6954  // will actually only
6955  // contain "X", since 0
6956  // is ignored in the
6957  // call to
6958  // GetAllTransactionNumbers.)
6959  // 3. Bob then checks
6960  // like this: if
6961  // (numlistOutpayment.VerifyAny(numlistIncomingPayment))
6962  // This is equivalent
6963  // to saying: if
6964  // ("X,Y".VerifyAny("X"))
6965  // which RETURNS TRUE --
6966  // and we have
6967  // found the
6968  // instrument!
6969 
6970  OTPayment theOutpayment;
6971 
6972  if (strInstrument
6973  .Exists() &&
6974  theOutpayment
6975  .SetPayment(
6976  strInstrument) &&
6977  theOutpayment
6978  .SetTempValues()) {
6979  theOutpayment
6981  numlistOutpayment);
6982  }
6983 
6984  const int32_t nTransCount =
6985  thePmntInbox
6986  .GetTransactionCount();
6987 
6988  for (int32_t ii =
6989  (nTransCount -
6990  1);
6991  ii >= 0;
6992  --ii) // Count
6993  // backwards
6994  // since we
6995  // are
6996  // removing
6997  // things.
6998  {
6999  std::unique_ptr<
7000  OTPayment>
7001  pPayment(GetInstrument(
7002  *pNym, ii,
7003  thePmntInbox));
7004 
7005  if (nullptr ==
7006  pPayment) {
7007  otOut
7008  << __FUNCTION__
7009  << ": "
7010  "(Upon "
7011  "receivi"
7012  "ng "
7013  "notice)"
7014  " While "
7015  "looping"
7016  " paymen"
7017  "ts "
7018  "inbox "
7019  "to "
7020  "remove "
7021  "a "
7022  "payment"
7023  ", "
7024  "unable "
7025  "to "
7026  "retriev"
7027  "e "
7028  "payment"
7029  " at "
7030  "index "
7031  << ii
7032  << " ("
7033  "skippin"
7034  "g.)\n";
7035  continue;
7036  }
7037  else if (false ==
7038  pPayment
7039  ->SetTempValues()) {
7040  otOut
7041  << __FUNCTION__
7042  << ": "
7043  "(Upon "
7044  "receivi"
7045  "ng "
7046  "notice)"
7047  " While "
7048  "looping"
7049  " paymen"
7050  "ts "
7051  "inbox "
7052  "to "
7053  "remove "
7054  "a "
7055  "payment"
7056  ", "
7057  "unable "
7058  "to set "
7059  "temp "
7060  "values "
7061  "for "
7062  "payment"
7063  " at "
7064  "index "
7065  << ii
7066  << " ("
7067  "skippin"
7068  "g.)\n";
7069  continue;
7070  }
7071 
7072  OTNumList
7073  numlistIncomingPayment;
7074 
7075  pPayment->GetAllTransactionNumbers(
7076  numlistIncomingPayment);
7077 
7078  if (numlistOutpayment
7079  .VerifyAny(
7080  numlistIncomingPayment)) // Found it.
7081  {
7082  // ** It's the
7083  // same
7084  // instrument.**
7085  // Remove it
7086  // from the
7087  // payments
7088  // inbox, and
7089  // save.
7090  //
7091  OTTransaction*
7092  pTransPaymentInbox =
7093  thePmntInbox
7094  .GetTransactionByIndex(
7095  ii);
7096  OT_ASSERT(
7097  nullptr !=
7098  pTransPaymentInbox); // It DEFINITELY should be there. (Assert otherwise.)
7099  int64_t lPaymentTransNum =
7100  pTransPaymentInbox
7101  ->GetTransactionNum();
7102 
7103  // DON'T I NEED
7104  // to call
7105  // DeleteBoxReceipt
7106  // at this
7107  // point?
7108  // Since that
7109  // needs to be
7110  // called now
7111  // whenever
7112  // removing
7113  // something
7114  // from any box?
7115  //
7116  // NOTE: might
7117  // need to just
7118  // MOVE this box
7119  // receipt to
7120  // the record
7121  // box, instead
7122  // of
7123  // deleting it.
7124  //
7125  // Probably I
7126  // need to do
7127  // that ONLY if
7128  // the version
7129  // in the
7130  // payments
7131  // outbox
7132  // doesn't
7133  // exist.
7134  // For example,
7135  // if
7136  // strInstrument
7137  // doesn't
7138  // exist, then
7139  // there was
7140  // nothing in
7141  // the payments
7142  // outbox, and
7143  // therefore the
7144  // version in
7145  // the payment
7146  // INBOX is the
7147  // ONLY version
7148  // I have,
7149  // and therefore
7150  // I should
7151  // stick it in
7152  // the Record
7153  // Box.
7154  //
7155  // HOWEVER, if
7156  // strInstrument
7157  // DOES exist,
7158  // then I should
7159  // create its
7160  // own
7161  // transaction
7162  // to add
7163  // to the record
7164  // box, and
7165  // delete the
7166  // one that was
7167  // in the
7168  // payment
7169  // inbox. Why
7170  // delete it?
7171  // Because
7172  // otherwise I
7173  // would be
7174  // adding the
7175  // same thing
7176  // TWICE to the
7177  // record box,
7178  // which I don't
7179  // really
7180  // need to do.
7181  // And if I'm
7182  // going to
7183  // choose one of
7184  // the two, the
7185  // one in the
7186  // outpayments
7187  // box will
7188  // be the more
7189  // recent / more
7190  // relevant one
7191  // of the two.
7192  // So I favor
7193  // that one,
7194  // unless it
7195  // doesn't
7196  // exist, in
7197  // which case I
7198  // should add
7199  // the other one
7200  // instead.
7201  // (Todo.)
7202  //
7203  // NOTE: Until
7204  // the above is
7205  // completed,
7206  // the current
7207  // behavior is
7208  // that the
7209  // outpayments
7210  // box item
7211  // will be moved
7212  // to the record
7213  // box if it
7214  // exists, and
7215  // otherwise
7216  // nothing will
7217  // be, since any
7218  // payments
7219  // inbox item
7220  // will be
7221  // deleted.
7222 
7223  if (false ==
7224  thePmntInbox
7225  .DeleteBoxReceipt(
7226  lPaymentTransNum)) {
7227  otErr
7228  << __FUNCTION__
7229  << ": "
7230  "Fai"
7231  "led"
7232  " tr"
7233  "yin"
7234  "g "
7235  "to "
7236  "del"
7237  "ete"
7238  " th"
7239  "e "
7240  "box"
7241  " re"
7242  "cei"
7243  "pt "
7244  "for"
7245  " a "
7246  "tra"
7247  "nsa"
7248  "cti"
7249  "on "
7250  "bei"
7251  "ng "
7252  "rem"
7253  "ove"
7254  "d "
7255  "fro"
7256  "m "
7257  "the"
7258  " pa"
7259  "yme"
7260  "nt "
7261  "inb"
7262  "ox."
7263  "\n";
7264  }
7265  if (thePmntInbox
7266  .RemoveTransaction(
7267  lPaymentTransNum)) {
7268  thePmntInbox
7269  .ReleaseSignatures();
7270  thePmntInbox
7271  .SignContract(
7272  *pNym);
7273  thePmntInbox
7274  .SaveContract();
7275 
7276  if (!thePmntInbox
7277  .SavePaymentInbox()) {
7278  otErr
7279  << __FUNCTION__
7280  << ": Failure while trying to save payment inbox.\n";
7281  }
7282  else {
7283  otOut
7284  << __FUNCTION__
7285  << ": Removed instrument from payment inbox.\nSaved payment inbox.\n";
7286  }
7287  }
7288  else {
7289  otErr
7290  << __FUNCTION__
7291  << ": "
7292  "Fai"
7293  "led"
7294  " tr"
7295  "yin"
7296  "g "
7297  "to "
7298  "rem"
7299  "ove"
7300  " tr"
7301  "ans"
7302  "act"
7303  "ion"
7304  " fr"
7305  "om "
7306  "pay"
7307  "men"
7308  "t "
7309  "inb"
7310  "ox."
7311  " ("
7312  "Sho"
7313  "uld"
7314  " ne"
7315  "ver"
7316  " ha"
7317  "ppe"
7318  "n.)"
7319  "\n";
7320  }
7321  // Todo: save a
7322  // copy to the
7323  // record box.
7324  // Note: I could
7325  // break right
7326  // here, if this
7327  // is the only
7328  // transaction
7329  // in the
7330  // payment inbox
7331  // which
7332  // contains the
7333  // instrument in
7334  // question.
7335  // Which I
7336  // believe
7337  // it is. Todo:
7338  // if that's
7339  // true, which I
7340  // think it is,
7341  // then call
7342  // break here.
7343  // After all,
7344  // you wouldn't
7345  // send me the
7346  // SAME
7347  // instrument
7348  // TWICE, would
7349  // you?
7350  // But it still
7351  // seems
7352  // theoretically
7353  // possible
7354  // (albeit
7355  // stupid.)
7356  }
7357  }
7358  // for (int32_t ii = 0;
7359  // ii < nTransCount;
7360  // ++ii)
7361  // Also, if there was a
7362  // message in the
7363  // outpayments box
7364  // (which we already
7365  // removed
7366  // a bit above), go
7367  // ahead and add a
7368  // receipt for it into
7369  // the record box.
7370  //
7371  if (strInstrument
7372  .Exists()) // Found
7373  // the
7374  // instrument
7375  // in
7376  // the
7377  // outpayments
7378  // box.
7379  {
7380  OTTransaction* pNewTransaction =
7382  theRecordBox, // recordbox.
7383  OTTransaction::
7384  notice,
7385  lNymOpeningNumber);
7386  std::unique_ptr<
7387  OTTransaction>
7388  theTransactionAngel(
7389  pNewTransaction);
7390 
7391  if (nullptr !=
7392  pNewTransaction) // The above has an OT_ASSERT within, but I just like to check my pointers.
7393  {
7394  pNewTransaction
7396  lNymOpeningNumber); // Referencing myself here. We'll see how it works out.
7397  pNewTransaction
7399  strInstrument); // The cheque, invoice, etc that used to be in the outpayments box.
7400  pNewTransaction
7401  ->SignContract(
7402  *pNym);
7403  pNewTransaction
7404  ->SaveContract();
7405  const bool bAdded =
7406  theRecordBox
7407  .AddTransaction(
7408  *pNewTransaction);
7409 
7410  if (!bAdded) {
7411  otErr
7412  << __FUNCTION__
7413  << ": "
7414  "Una"
7415  "ble"
7416  " to"
7417  " ad"
7418  "d "
7419  "tra"
7420  "nsa"
7421  "cti"
7422  "on "
7423  << pNewTransaction
7424  ->GetTransactionNum()
7425  << " to"
7426  " re"
7427  "cor"
7428  "d "
7429  "box"
7430  " ("
7431  "aft"
7432  "er "
7433  "ten"
7434  "tat"
7435  "ive"
7436  "ly "
7437  "rem"
7438  "ovi"
7439  "ng "
7440  "fro"
7441  "m "
7442  "pay"
7443  "men"
7444  "t "
7445  "out"
7446  "box"
7447  ", "
7448  "an "
7449  "act"
7450  "ion"
7451  " th"
7452  "at "
7453  "is "
7454  "now"
7455  " ca"
7456  "nce"
7457  "led"
7458  ".)"
7459  "\n";
7460  return false;
7461  }
7462  else
7463  theTransactionAngel
7464  .release(); // If successfully added to the record box, then no need anymore to clean it up ourselves. The record box owns it now.
7465 
7466  theRecordBox
7467  .ReleaseSignatures();
7468  theRecordBox
7469  .SignContract(
7470  *pNym);
7471  theRecordBox
7472  .SaveContract();
7473  theRecordBox
7474  .SaveRecordBox(); // todo log failure.
7475 
7476  // Any
7477  // inbox/nymbox/outbox
7478  // ledger will
7479  // only itself
7480  // contain
7481  // abbreviated
7482  // versions of
7483  // the receipts,
7484  // including
7485  // their hashes.
7486  //
7487  // The rest is
7488  // stored
7489  // separately,
7490  // in the box
7491  // receipt,
7492  // which is
7493  // created
7494  // whenever a
7495  // receipt is
7496  // added to a
7497  // box, and
7498  // deleted after
7499  // a receipt
7500  // is removed
7501  // from a box.
7502  //
7503  if (!pNewTransaction
7504  ->SaveBoxReceipt(
7505  theRecordBox)) // <===================
7506  {
7507  OTString strNewTransaction(
7508  *pNewTransaction);
7509  otErr
7510  << __FUNCTION__
7511  << ": "
7512  "for"
7513  " Re"
7514  "cor"
7515  "d "
7516  "Box"
7517  "..."
7518  " Fa"
7519  "ile"
7520  "d "
7521  "try"
7522  "ing"
7523  " to"
7524  " Sa"
7525  "veB"
7526  "oxR"
7527  "ece"
7528  "ipt"
7529  ". "
7530  "Con"
7531  "ten"
7532  "ts:"
7533  "\n"
7534  "\n"
7535  << strNewTransaction
7536  << "\n"
7537  "\n";
7538  }
7539  }
7540  else // should
7541  // never
7542  // happen
7543  {
7544  otErr
7545  << __FUNCTION__
7546  << ": "
7547  "Failed "
7548  "while "
7549  "trying "
7550  "to "
7551  "generat"
7552  "e "
7553  "transac"
7554  "tion "
7555  "in "
7556  "order "
7557  "to add "
7558  "a new "
7559  "transac"
7560  "tion "
7561  "to "
7562  "record "
7563  "box "
7564  "(for a "
7565  "payment"
7566  " instru"
7567  "ment "
7568  "we "
7569  "just "
7570  "removed"
7571  " from "
7572  "the "
7573  "outpaym"
7574  "ents "
7575  "box): "
7576  << strNymID
7577  << "\n";
7578  }
7579  } // if
7580  // (strInstrument.Exists())
7581  // (then add a copy to
7582  // record box.)
7583  } // else (Success loading
7584  // the payment inbox and
7585  // recordBox)
7586  } // (OTItem::rejection ==
7587  // pReplyItem->GetStatus())
7588  } // if (!bIsActivatingNym)
7589  } // if (nullptr != pCronItem)
7590  else {
7591  otErr << __FUNCTION__
7592  << ": Error loading cronitem "
7593  "from Nymbox receipt, "
7594  "from string:\n"
7595  << strCronItem << "\n";
7596  }
7597 
7598  } // pReplyItem is a rejection.
7599  } // pServerTransaction (the Nymbox receipt we
7600  // just accepted / removed) is a notice.
7601 
7602  break;
7603 
7606  break;
7607  // I don't think we need to do anything here...
7608 
7610  otInfo
7611  << __FUNCTION__
7612  << ": Successfully removed finalReceipt "
7613  "from Nymbox with opening num: "
7614  << pServerTransaction->GetReferenceToNum()
7615  << "\n";
7616 
7617  if (pNym->RemoveIssuedNum(
7618  *pNym, strServerID,
7619  pServerTransaction->GetReferenceToNum(),
7620  true)) // bool bSave=true
7621  otWarn << "**** Due to finding a "
7622  "finalReceipt, REMOVING OPENING "
7623  "NUMBER FROM NYM: "
7624  << pServerTransaction
7625  ->GetReferenceToNum()
7626  << " \n";
7627  else
7628  otWarn << "**** Noticed a finalReceipt, "
7629  "but Opening Number "
7630  << pServerTransaction
7631  ->GetReferenceToNum()
7632  << " had ALREADY been removed from "
7633  "nym. \n";
7634 
7635  // BUG: RemoveIssuedNum shouldn't be here. In
7636  // Nymbox, finalReceipt is only a notice, and I
7637  // shoulda
7638  // removed the number the instant that I saw it.
7639  // (Back when processing the Nymbox, before even
7640  // calculating the request.) Therefore, this is
7641  // moved to AcceptEntireNymbox and Finalize for
7642  // Process Inbox.
7643  //
7644  // pNym->RemoveIssuedNum(*pNym,
7645  // strServerID,
7646  // pServerTransaction->GetReferenceToNum(),
7647  // true); // bool bSave=true
7648  // The client side keeps a list of active
7649  // (recurring) transactions.
7650  // That is, smart contracts and payment plans. I
7651  // don't think it keeps
7652  // market offers in that list, since we already
7653  // have a list of active
7654  // market offers separately. And market offers
7655  // produce final receipts,
7656  // so basically this piece of code will be
7657  // executed for all final receipts.
7658  // It's not really necessary that it be called
7659  // for market offers, but whatever.
7660  // It is for the others.
7661  //
7662  // Notice even though the final receipt hasn't
7663  // yet been cleared out of the box,
7664  // we are already removing the record of the
7665  // active cron receipt. Why?
7666  // Because regardless of when the user processes
7667  // the finalReceipt, we know for
7668  // a fact the transaction is no longer actively
7669  // running on Cron. So we don't want
7670  // to keep it on our list of "active" cron items
7671  // if we know it's already inactive.
7672  //
7674  pServerTransaction->GetReferenceToNum(),
7675  pNym->GetConstID(),
7676  pServerTransaction->GetPurportedServerID());
7677 
7678  break;
7679 
7680  default: {
7681  OTString strTempTypeString;
7682  pReplyItem->GetTypeString(strTempTypeString);
7683  otErr << "Unexpected replyItem:type while "
7684  "processing Nymbox: "
7685  << strTempTypeString << " \n";
7686  continue;
7687  }
7688  } // switch replyItem type
7689 
7690  // Remove from pNymbox
7691  // This happens for ALL of the above cases.
7692  // Update: Now whenever removing a receipt from any
7693  // box, we also have
7694  // to delete the box receipt, which is stored as a
7695  // separate file.
7696  //
7697  pServerTransaction->DeleteBoxReceipt(
7698  *pNymbox); // faster.
7699  // pNymbox->DeleteBoxReceipt(pServerTransaction->GetTransactionNum());
7700  pNymbox->RemoveTransaction(
7701  pServerTransaction->GetTransactionNum());
7702 
7703  } // for loop (reply items)
7704  // All done? Let's save up...
7705  //
7706  pNymbox->ReleaseSignatures();
7707  pNymbox->SignContract(*pNym);
7708  pNymbox->SaveContract();
7709  pNymbox->SaveNymbox();
7710 
7711  pNymbox = nullptr; // Since it could be pointing to a
7712  // variable that's in this block (now
7713  // out of scope) then we clear the
7714  // pointer.
7715  } // pTransaction and pReplyTransaction are both NOT
7716  // nullptr.
7717  }
7718 
7719  //
7720  // The below happens BOTH for Inbox AND Nymbox.
7721 
7722  if ((nullptr != pTransaction) &&
7723  (nullptr != pReplyTransaction)) {
7724  //
7725  // SAVE THE RECEIPT....
7726 
7727  OTString strServerID(SERVER_ID);
7728 
7729  OTString strReceiptID("NOT_SET_YET");
7730 
7731  OTItem* pReplyItem =
7732  pReplyTransaction->GetItem(OTItem::atBalanceStatement);
7733 
7734  if (nullptr == pReplyItem) {
7735  pReplyItem = pReplyTransaction->GetItem(
7737 
7738  if (nullptr != pReplyItem)
7739  pNym->GetIdentifier(strReceiptID); // In this case,
7740  // the receipt ID
7741  // is the Nym ID
7742  }
7743  else {
7744  strReceiptID =
7745  theReply.m_strAcctID; // If a balance statement,
7746  // then the receipt ID is the
7747  // Account ID.
7748  }
7749  OTString strTransaction;
7750  pReplyTransaction->SaveContractRaw(
7751  strTransaction); // <=========== Save that receipt!
7752  OTString strReceiptFilename;
7753 
7754  if (pReplyTransaction->GetSuccess())
7755  strReceiptFilename.Format("%s.success",
7756  strReceiptID.Get());
7757  else
7758  strReceiptFilename.Format("%s.fail",
7759  strReceiptID.Get());
7760  OTString strFinal;
7761  OTASCIIArmor ascTemp(strTransaction);
7762 
7763  if (false ==
7764  ascTemp.WriteArmoredString(
7765  strFinal, "TRANSACTION")) // todo hardcoding.
7766  {
7767  otErr << "OTClient::ProcessServerReply: Error saving "
7768  "transaction receipt (failed writing armored "
7769  "string):\n" << OTFolders::Receipt()
7770  << OTLog::PathSeparator() << strServerID
7771  << OTLog::PathSeparator() << strReceiptFilename
7772  << "\n Contents:\n" << strTransaction << "\n";
7773  }
7774  else // success writing armored string
7775  {
7776  if (nullptr != pReplyItem) {
7778  strFinal.Get(), OTFolders::Receipt().Get(),
7779  strServerID.Get(), strReceiptFilename.Get());
7780  }
7781  else // This should never happen...
7782  {
7783  strReceiptFilename.Format("%s.error",
7784  strReceiptID.Get());
7785 
7786  otErr << "OTClient::ProcessServerReply: Error "
7787  "saving transaction receipt: "
7788  << strServerID << OTLog::PathSeparator()
7789  << strReceiptFilename << "\n";
7790 
7792  strFinal.Get(), OTFolders::Receipt().Get(),
7793  strServerID.Get(), strReceiptFilename.Get());
7794  }
7795  } // success writing armored string
7796  }
7797  else {
7798  const OTString strTheLedger(theLedger),
7799  strTheReplyLedger(theReplyLedger);
7800  otOut << "Strange... found ledger in "
7801  << theReply.m_strCommand
7802  << ", but didn't find the right transaction type "
7803  "within.\n(pTransaction == "
7804  << ((nullptr != pTransaction) ? "NOT nullptr"
7805  : "nullptr")
7806  << ") && (pReplyTransaction == "
7807  << ((nullptr != pReplyTransaction) ? "NOT nullptr"
7808  : "nullptr")
7809  << ")\ntheLedger: \n\n" << strTheLedger
7810  << "\n\ntheReplyLedger:\n\n" << strTheReplyLedger
7811  << "\n\n";
7812  }
7813  }
7814  }
7815  else {
7816  otOut << "Strange... received server acknowledgment but 'in "
7817  "reference to' message was blank.\n";
7818  }
7819 
7820  return true;
7821  }
7822  else if (theReply.m_bSuccess &&
7823  theReply.m_strCommand.Compare("@getAccountFiles")) // Replaces
7824  // getAccount,
7825  // getInbox,
7826  // and
7827  // getOutbox
7828  {
7829  otOut << "Received server response to getAccountFiles message.\n";
7830 
7831  OTASCIIArmor& ascArmor = theReply.m_ascPayload; // containing account
7832  // file + inbox and
7833  // outbox.
7834  const bool bHasFiles = ascArmor.Exists();
7835  if (bHasFiles) {
7836  std::unique_ptr<OTDB::Storable> pStorable(OTDB::DecodeObject(
7837  OTDB::STORED_OBJ_STRING_MAP, ascArmor.Get()));
7838  OTDB::StringMap* pMap =
7839  dynamic_cast<OTDB::StringMap*>(pStorable.get());
7840  if (nullptr == pMap)
7841  otOut << __FUNCTION__ << ": Failed decoding StringMap object "
7842  "in @getAccountFiles.\n";
7843  else {
7844  OTString::Map& theMap = pMap->the_map;
7845  OTString strAccount, strInbox, strOutbox;
7846  auto it_account = theMap.find("account");
7847  auto it_inbox = theMap.find("inbox");
7848  auto it_outbox = theMap.find("outbox");
7849  if ((theMap.end() != it_account) &&
7850  (it_account->second.size() > 0))
7851  strAccount = it_account->second.c_str();
7852  if ((theMap.end() != it_inbox) && (it_inbox->second.size() > 0))
7853  strInbox = it_inbox->second.c_str();
7854  if ((theMap.end() != it_outbox) &&
7855  (it_outbox->second.size() > 0))
7856  strOutbox = it_outbox->second.c_str();
7857  if (strAccount.Exists()) {
7858  // Load the account object from that string.
7859  std::unique_ptr<OTAccount> pAccount(
7860  new OTAccount(USER_ID, ACCOUNT_ID, SERVER_ID));
7861 
7862  if (pAccount &&
7863  pAccount->LoadContractFromString(strAccount) &&
7864  pAccount->VerifyAccount(*pServerNym)) {
7865  otInfo << "Saving updated account file to disk...\n";
7866  pAccount->ReleaseSignatures(); // So I don't get the
7867  // annoying failure to
7868  // verify message from
7869  // the server's
7870  // signature.
7871  // Will eventually end up keeping the signature,
7872  // however, just for reasons of proof.
7873  // UPDATE (above) I now release signatures again since
7874  // we have receipts functional. As long as receipt has
7875  // server's signature, it can prove the others.
7876  pAccount->SignContract(*pNym);
7877  pAccount->SaveContract();
7878  pAccount->SaveAccount();
7879 
7880  // Next let's make sure the wallet's copy of this
7881  // account is replaced with the new one...
7882  OTWallet* pWallet = theConnection.GetWallet();
7883 
7884  if (nullptr != pWallet) {
7885  pWallet->AddAccount(*(pAccount.release()));
7886  pWallet->SaveWallet();
7887  }
7888  }
7889  }
7890 
7891  const OTString strAcctID(ACCOUNT_ID);
7892  const std::string str_acct_id(strAcctID.Get());
7893 
7894  if (strInbox.Exists()) {
7895  const OTString strServerID(SERVER_ID);
7896 
7897  // Load the ledger object from strInbox
7898  OTLedger theInbox(USER_ID, ACCOUNT_ID, SERVER_ID);
7899 
7900  // I receive the inbox, verify the server's signature, then
7901  // RE-SIGN IT WITH MY OWN
7902  // SIGNATURE, then SAVE it to local storage. So any FUTURE
7903  // checks of this inbox
7904  // would require MY signature, not the server's, to verify.
7905  // But in this one spot,
7906  // just before saving, I need to verify the server's first.
7907  // UPDATE: Keeping the server's signature, and just adding
7908  // my own.
7909  if (theInbox.LoadInboxFromString(strInbox) &&
7910  theInbox.VerifySignature(
7911  *pServerNym)) // No VerifyAccount. Can't, because
7912  // client hasn't had a chance yet to
7913  // download the box receipts that go
7914  // with this inbox -- and
7915  // VerifyAccount() tries to load
7916  // those, which would fail here...
7917  {
7918  OTIdentifier THE_HASH;
7919 
7920  if (theReply.m_strInboxHash.Exists()) {
7921  THE_HASH.SetString(theReply.m_strInboxHash);
7922 
7923  const bool bHash =
7924  pNym->SetInboxHash(str_acct_id, THE_HASH);
7925 
7926  if (!bHash)
7927  otErr << __FUNCTION__
7928  << ": Failed setting InboxHash on Nym "
7929  "for account: " << str_acct_id << "\n";
7930  else {
7931  OTPseudonym* pSignerNym = pNym;
7932  pNym->SaveSignedNymfile(*pSignerNym);
7933  }
7934  }
7935 
7936  // If I have Transaction #35 signed out, and I use it to
7937  // start a market offer (or any other cron item)
7938  // then it's always possible that a finalReceipt will
7939  // pop into my Inbox while I'm asleep, closing
7940  // that transaction #. The server officially believes 35
7941  // is closed. Unfortunately, I still have it signed
7942  // out, on my side anyway, because I didn't know the
7943  // finalReceipt came in.
7944  //
7945  // THEREFORE, WHEN A FINAL RECEIPT COMES IN, I NEED TO
7946  // REMOVE ITS "in reference to" NUMBER FROM MY
7947  // ISSUED LIST. Here is clearly the best place for that:
7948  //
7949  for (auto& it : theInbox.GetTransactionMap()) {
7950  OTTransaction* pTempTrans = it.second;
7951  OT_ASSERT(nullptr != pTempTrans);
7952 
7953  // TODO security: Keep a client-side list of issued
7954  // #s for finalReceipts. That way,
7955  // I'll be smart enough here not to actually remove
7956  // just any number, unless it's actually
7957  // on my list of final receipts. (The server does a
7958  // similar thing already.)
7959  //
7961  pTempTrans->GetType()) {
7962  otInfo << "*** Removing opening issued number ("
7963  << pTempTrans->GetReferenceToNum()
7964  << "), since finalReceipt found when "
7965  "retrieving asset account inbox. "
7966  "***\n";
7967 
7968  if (pNym->RemoveIssuedNum(
7969  *pNym, strServerID,
7970  pTempTrans->GetReferenceToNum(),
7971  true)) // bool bSave=true
7972  otWarn << "**** Due to finding a "
7973  "finalReceipt, REMOVING OPENING "
7974  "NUMBER FROM NYM: "
7975  << pTempTrans->GetReferenceToNum()
7976  << " \n";
7977  else
7978  otWarn << "**** Noticed a finalReceipt, "
7979  "but Opening Number "
7980  << pTempTrans->GetReferenceToNum()
7981  << " had ALREADY been removed from "
7982  "nym. \n";
7983 
7984  // pNym->RemoveIssuedNum(*pNym,
7985  // strServerID, pTempTrans->GetReferenceToNum(),
7986  // true); // bSave = true;
7987  // The client side keeps a list of active
7988  // (recurring) transactions.
7989  // That is, smart contracts and payment plans. I
7990  // don't think it keeps
7991  // market offers in that list, since we already
7992  // have a list of active
7993  // market offers separately. And market offers
7994  // produce final receipts,
7995  // so basically this piece of code will be
7996  // executed for all final receipts.
7997  // It's not really necessary that it be called
7998  // for market offers, but whatever.
7999  // It is for the others.
8000  //
8002  pTempTrans->GetReferenceToNum(),
8003  pNym->GetConstID(),
8004  pTempTrans->GetPurportedServerID());
8005 
8006  } // We also do this in AcceptEntireNymbox
8007  }
8008 
8009  // Now I'm keeping the server signature, and just adding
8010  // my own.
8011  theInbox.ReleaseSignatures(); // This is back. Why?
8012  // Because we have
8013  // receipts functional
8014  // now.
8015  theInbox.SignContract(*pNym);
8016  theInbox.SaveContract();
8017  theInbox.SaveInbox();
8018  }
8019  else {
8020  otErr << __FUNCTION__
8021  << ": Error loading (from string) or verifying "
8022  "inbox:\n\n" << strInbox << "\n";
8023  }
8024  }
8025  if (strOutbox.Exists()) {
8026  // Load the ledger object from strOutbox.
8027  OTLedger theOutbox(USER_ID, ACCOUNT_ID, SERVER_ID);
8028 
8029  // I receive the outbox, verify the server's signature, then
8030  // RE-SIGN IT WITH MY OWN
8031  // SIGNATURE, then SAVE it to local storage. So any FUTURE
8032  // checks of this outbox
8033  // would require MY signature, not the server's, to verify.
8034  // But in this one spot,
8035  // just before saving, I need to verify the server's first.
8036  // UPDATE: keeping the server's signature, and just adding
8037  // my own.
8038  if (theOutbox.LoadOutboxFromString(strOutbox) &&
8039  theOutbox.VerifySignature(
8040  *pServerNym)) // No point calling VerifyAccount
8041  // since the client hasn't even had a
8042  // chance to download the box receipts
8043  // yet...
8044  {
8045  OTIdentifier THE_HASH;
8046 
8047  if (theReply.m_strOutboxHash.Exists()) {
8048  THE_HASH.SetString(theReply.m_strOutboxHash);
8049 
8050  const bool bHash =
8051  pNym->SetOutboxHash(str_acct_id, THE_HASH);
8052 
8053  if (!bHash)
8054  otErr << __FUNCTION__
8055  << ": Failed setting OutboxHash on Nym "
8056  "for account: " << str_acct_id << "\n";
8057  else {
8058  OTPseudonym* pSignerNym = pNym;
8059  pNym->SaveSignedNymfile(*pSignerNym);
8060  }
8061  }
8062  theOutbox.ReleaseSignatures(); // UPDATE: keeping the
8063  // server's signature,
8064  // and just adding my
8065  // own.
8066  theOutbox.SignContract(*pNym); // ANOTHER UPDATE:
8067  // Removing signature
8068  // again, since we have
8069  // receipts functional
8070  // now.
8071  theOutbox.SaveContract();
8072  theOutbox.SaveOutbox();
8073  }
8074  else {
8075  otErr << __FUNCTION__
8076  << ": Error loading (from string) or verifying "
8077  "outbox:\n\n" << strOutbox << "\n";
8078  }
8079  }
8080  } // pMap loaded successfully.
8081  } // Has the files.
8082 
8083  return true;
8084  }
8085  else if (theReply.m_bSuccess &&
8086  theReply.m_strCommand.Compare(
8087  "@getAccount")) // Deprecated. (Replaced by getAccountFiles.)
8088  {
8089  // base64-Decode the server reply's payload into strAccount
8090  OTString strAccount(theReply.m_ascPayload);
8091 
8092  // Load the account object from that string.
8093  std::unique_ptr<OTAccount> pAccount(
8094  new OTAccount(USER_ID, ACCOUNT_ID, SERVER_ID));
8095 
8096  if (pAccount && pAccount->LoadContractFromString(strAccount) &&
8097  pAccount->VerifyAccount(*pServerNym)) {
8098  otInfo << "Saving updated account file to disk...\n";
8099  pAccount->ReleaseSignatures(); // So I don't get the annoying
8100  // failure to verify message from the
8101  // server's signature.
8102  // Will eventually end up keeping the signature, however, just for
8103  // reasons of proof.
8104  // UPDATE (above) I now release signatures again since we have
8105  // receipts functional. As long as receipt has server's signature,
8106  // it can prove the others.
8107  pAccount->SignContract(*pNym);
8108  pAccount->SaveContract();
8109  pAccount->SaveAccount();
8110 
8111  // Next let's make sure the wallet's copy of this account is
8112  // replaced with the new one...
8113  OTWallet* pWallet = theConnection.GetWallet();
8114 
8115  if (nullptr != pWallet) {
8116  pWallet->AddAccount(*(pAccount.release()));
8117  pWallet->SaveWallet();
8118  }
8119  }
8120  return true;
8121  }
8122  else if (theReply.m_bSuccess &&
8123  theReply.m_strCommand.Compare(
8124  "@getInbox")) // Deprecated. (Replaced by getAccountFiles.)
8125  {
8126  const OTString strServerID(SERVER_ID);
8127 
8128  otWarn << "Received server response to Get Inbox message.\n";
8129 
8130  // base64-Decode the server reply's payload into strInbox
8131  OTString strInbox(theReply.m_ascPayload);
8132 
8133  // Load the ledger object from that string.
8134  OTLedger theInbox(USER_ID, ACCOUNT_ID, SERVER_ID);
8135 
8136  // I receive the inbox, verify the server's signature, then RE-SIGN IT
8137  // WITH MY OWN
8138  // SIGNATURE, then SAVE it to local storage. So any FUTURE checks of
8139  // this inbox
8140  // would require MY signature, not the server's, to verify. But in this
8141  // one spot,
8142  // just before saving, I need to verify the server's first.
8143  // UPDATE: Keeping the server's signature, and just adding my own.
8144  if (theInbox.LoadInboxFromString(strInbox) &&
8145  theInbox.VerifySignature(*pServerNym)) // No VerifyAccount. Can't,
8146  // because client hasn't had
8147  // a chance yet to download
8148  // the box receipts that go
8149  // with this inbox -- and
8150  // VerifyAccount() tries to
8151  // load those, which would
8152  // fail here...
8153  {
8154  OTIdentifier THE_HASH;
8155  const OTString strAcctID(ACCOUNT_ID);
8156  const std::string str_acct_id(strAcctID.Get());
8157 
8158  if (theReply.m_strInboxHash.Exists()) {
8159  THE_HASH.SetString(theReply.m_strInboxHash);
8160 
8161  const bool bHash = pNym->SetInboxHash(str_acct_id, THE_HASH);
8162 
8163  if (!bHash)
8164  otErr << "Failed setting InboxHash on Nym for account: "
8165  << str_acct_id << "\n";
8166  else {
8167  OTPseudonym* pSignerNym = pNym;
8168  pNym->SaveSignedNymfile(*pSignerNym);
8169  }
8170  }
8171 
8172  // If I have Transaction #35 signed out, and I use it to start a
8173  // market offer (or any other cron item)
8174  // then it's always possible that a finalReceipt will pop into my
8175  // Inbox while I'm asleep, closing
8176  // that transaction #. The server officially believes 35 is closed.
8177  // Unfortunately, I still have it signed
8178  // out, on my side anyway, because I didn't know the finalReceipt
8179  // came in.
8180  //
8181  // THEREFORE, WHEN A FINAL RECEIPT COMES IN, I NEED TO REMOVE ITS
8182  // "in reference to" NUMBER FROM MY
8183  // ISSUED LIST. Here is clearly the best place for that:
8184  //
8185  for (auto& it : theInbox.GetTransactionMap()) {
8186  OTTransaction* pTempTrans = it.second;
8187  OT_ASSERT(nullptr != pTempTrans);
8188 
8189  // TODO security: Keep a client-side list of issued #s for
8190  // finalReceipts. That way,
8191  // I'll be smart enough here not to actually remove just any
8192  // number, unless it's actually
8193  // on my list of final receipts. (The server does a similar
8194  // thing already.)
8195  //
8196  if (OTTransaction::finalReceipt == pTempTrans->GetType()) {
8197  otInfo << "*** Removing opening issued number ("
8198  << pTempTrans->GetReferenceToNum()
8199  << "), since finalReceipt found when getting asset "
8200  "account inbox. ***\n";
8201 
8202  if (pNym->RemoveIssuedNum(*pNym, strServerID,
8203  pTempTrans->GetReferenceToNum(),
8204  true)) // bool bSave=true
8205  otWarn << "**** Due to finding a finalReceipt, "
8206  "REMOVING OPENING NUMBER FROM NYM: "
8207  << pTempTrans->GetReferenceToNum() << " \n";
8208  else
8209  otWarn << "**** Noticed a finalReceipt, but Opening "
8210  "Number " << pTempTrans->GetReferenceToNum()
8211  << " had ALREADY been removed from nym. \n";
8212 
8213  // pNym->RemoveIssuedNum(*pNym,
8214  // strServerID, pTempTrans->GetReferenceToNum(), true); //
8215  // bSave = true;
8216 
8217  // The client side keeps a list of active (recurring)
8218  // transactions.
8219  // That is, smart contracts and payment plans. I don't think
8220  // it keeps
8221  // market offers in that list, since we already have a list
8222  // of active
8223  // market offers separately. And market offers produce final
8224  // receipts,
8225  // so basically this piece of code will be executed for all
8226  // final receipts.
8227  // It's not really necessary that it be called for market
8228  // offers, but whatever.
8229  // It is for the others.
8230  //
8231  // Notice even though the final receipt hasn't yet been
8232  // cleared out of the box,
8233  // we are already removing the record of the active cron
8234  // receipt. Why?
8235  // Because regardless of when the user processes the
8236  // finalReceipt, we know for
8237  // a fact the transaction is no longer actively running on
8238  // Cron. So we don't want
8239  // to keep it on our list of "active" cron items if we know
8240  // it's already inactive.
8241  //
8243  pTempTrans->GetReferenceToNum(), pNym->GetConstID(),
8244  pTempTrans->GetPurportedServerID());
8245 
8246  } // We also do this in AcceptEntireNymbox
8247  }
8248 
8249  // Now I'm keeping the server signature, and just adding my own.
8250  theInbox.ReleaseSignatures(); // This is back. Why? Because we have
8251  // receipts functional now.
8252  theInbox.SignContract(*pNym);
8253  theInbox.SaveContract();
8254  theInbox.SaveInbox();
8255  }
8256  else {
8257  otErr << "Error loading (from string) or verifying inbox:\n\n"
8258  << strInbox << "\n";
8259  }
8260 
8261  return true;
8262  }
8263  else if (theReply.m_bSuccess &&
8264  theReply.m_strCommand.Compare(
8265  "@getOutbox")) // Deprecated. (Replaced by getAccountFiles.)
8266  {
8267 
8268  otWarn << "Received server response to Get Outbox message.\n";
8269  // OTString strReply(theReply);
8270  // otOut << "Received server response to Get Outbox
8271  // message:\n" << strReply << "\n";
8272 
8273  // base64-Decode the server reply's payload into strOutbox
8274  OTString strOutbox(theReply.m_ascPayload);
8275 
8276  // otErr << "OUTBOX CONTENTS:\n" << strOutbox << "\n";
8277 
8278  // Load the ledger object from that string.
8279  OTLedger theOutbox(USER_ID, ACCOUNT_ID, SERVER_ID);
8280 
8281  // I receive the outbox, verify the server's signature, then RE-SIGN IT
8282  // WITH MY OWN
8283  // SIGNATURE, then SAVE it to local storage. So any FUTURE checks of
8284  // this outbox
8285  // would require MY signature, not the server's, to verify. But in this
8286  // one spot,
8287  // just before saving, I need to verify the server's first.
8288  // UPDATE: keeping the server's signature, and just adding my own.
8289  if (theOutbox.LoadOutboxFromString(strOutbox) &&
8290  theOutbox.VerifySignature(*pServerNym)) // No point calling
8291  // VerifyAccount since the
8292  // client hasn't even had a
8293  // chance to download the
8294  // box receipts yet...
8295  {
8296  OTIdentifier THE_HASH;
8297  const OTString strAcctID(ACCOUNT_ID);
8298  const std::string str_acct_id(strAcctID.Get());
8299 
8300  if (theReply.m_strOutboxHash.Exists()) {
8301  THE_HASH.SetString(theReply.m_strOutboxHash);
8302 
8303  const bool bHash = pNym->SetOutboxHash(str_acct_id, THE_HASH);
8304 
8305  if (!bHash)
8306  otErr << "Failed setting OutboxHash on Nym for account: "
8307  << str_acct_id << "\n";
8308  else {
8309  OTPseudonym* pSignerNym = pNym;
8310  pNym->SaveSignedNymfile(*pSignerNym);
8311  }
8312  }
8313  theOutbox.ReleaseSignatures(); // UPDATE: keeping the server's
8314  // signature, and just adding my own.
8315  theOutbox.SignContract(*pNym); // ANOTHER UPDATE: Removing signature
8316  // again, since we have receipts
8317  // functional now.
8318  theOutbox.SaveContract();
8319  theOutbox.SaveOutbox();
8320  }
8321  else {
8322  otErr << "Error loading (from string) or verifying outbox:\n\n"
8323  << strOutbox << "\n";
8324  }
8325 
8326  return true;
8327  }
8328  else if (theReply.m_bSuccess &&
8329  theReply.m_strCommand.Compare("@getContract")) {
8330  // base64-Decode the server reply's payload into strContract
8331  OTString strContract(theReply.m_ascPayload);
8332 
8333  OTString strFoldername(OTFolders::Contract().Get());
8334  OTString strFilename; // In this case the filename isn't actually used,
8335  // since SaveToContractFolder will
8336  // handle setting up the filename and overwrite it anyway. But I still
8337  // prefer to set it
8338  // up correctly, rather than pass a blank. I'm just funny like that.
8339  strFilename = theReply.m_strAssetID.Get();
8340 
8341  OTAssetContract* pContract =
8342  new OTAssetContract(theReply.m_strAssetID, strFoldername,
8343  strFilename, theReply.m_strAssetID);
8344 
8345  OT_ASSERT(nullptr != pContract);
8346 
8347  // Check the server signature on the contract here. (Perhaps the message
8348  // is good enough?
8349  // After all, the message IS signed by the server and contains the
8350  // Account.
8351  // if (pContract->LoadContract() && pContract->VerifyContract())
8352  if (pContract->LoadContractFromString(strContract) &&
8353  pContract->VerifyContract()) {
8354  // Next make sure the wallet has this contract on its list...
8355  OTWallet* pWallet = theConnection.GetWallet();
8356 
8357  if (nullptr != pWallet) {
8358  pWallet->AddAssetContract(*pContract);
8359  pContract =
8360  nullptr; // Success. The wallet "owns" it now, no need
8361  // to clean it up.
8362  }
8363  }
8364  // cleanup
8365  if (pContract) {
8366  delete pContract;
8367  pContract = nullptr;
8368  }
8369  return true;
8370  }
8371  else if (theReply.m_bSuccess &&
8372  theReply.m_strCommand.Compare("@getMint")) {
8373  // base64-Decode the server reply's payload into strMint
8374  OTString strMint(theReply.m_ascPayload);
8375  // Load the mint object from that string...
8376  std::unique_ptr<Mint> pMint(
8377  Mint::MintFactory(theReply.m_strServerID, theReply.m_strAssetID));
8378  OT_ASSERT(nullptr != pMint);
8379  // TODO check the server signature on the mint here...
8380  if (pMint->LoadContractFromString(strMint)) {
8381  otOut << "Saving mint file to disk...\n";
8382  pMint->SaveMint();
8383  }
8384  return true;
8385  }
8386  else if (theReply.m_bSuccess &&
8387  theReply.m_strCommand.Compare("@getMarketList")) {
8388  OTString strMarketDatafile;
8389  strMarketDatafile.Format("%s", "market_data.bin");
8390 
8391  OTDB::Storage* pStorage = OTDB::GetDefaultStorage();
8392  OT_ASSERT(nullptr != pStorage);
8393 
8394  // The reply is a SUCCESS, and the COUNT is 0 (empty list was returned.)
8395  // Since it was a success, but the list was empty, then we need to erase
8396  // the data file. (So when the file is loaded from storage, it will
8397  // correctly
8398  // display an empty list on the screen, instead of a list of outdated
8399  // items.)
8400  //
8401  if (theReply.m_lDepth == 0) {
8402  bool bSuccessErase = pStorage->EraseValueByKey(
8403  OTFolders::Market().Get(), // "markets"
8404  theReply.m_strServerID.Get(), // "markets/<serverID>"
8405  strMarketDatafile
8406  .Get()); // "markets/<serverID>/market_data.bin"
8407  if (!bSuccessErase)
8408  otErr << "Error erasing market list from market folder: "
8409  << strMarketDatafile << " \n";
8410 
8411  return true;
8412  }
8413 
8414  OTPayload thePayload;
8415 
8416  if ((theReply.m_ascPayload.GetLength() <= 2) ||
8417  (false == theReply.m_ascPayload.GetData(thePayload))) {
8418  otErr << "ProcessServerReply: unable to decode ascii-armored "
8419  "payload in @getMarketList reply.\n";
8420  return true;
8421  }
8422 
8423  // Unpack the market list...
8424 
8425  OTDB::OTPacker* pPacker =
8426  pStorage->GetPacker(); // No need to check for failure, since this
8427  // already ASSERTS. No need to cleanup
8428  // either.
8429 
8430  std::unique_ptr<OTDB::PackedBuffer> pBuffer(pPacker->CreateBuffer());
8431  OT_ASSERT(nullptr != pBuffer);
8432 
8433  pBuffer->SetData(
8434  static_cast<const uint8_t*>(thePayload.GetPayloadPointer()),
8435  thePayload.GetSize());
8436 
8437  std::unique_ptr<OTDB::MarketList> pMarketList(
8438  dynamic_cast<OTDB::MarketList*>(
8440 
8441  bool bUnpacked = pPacker->Unpack(*pBuffer, *pMarketList);
8442 
8443  if (!bUnpacked) {
8444  otErr << "Process Server Reply: Failed unpacking data for "
8445  "@getMarketList.\n";
8446  return true;
8447  }
8448 
8449  bool bSuccessStore = pStorage->StoreObject(
8450  *pMarketList, OTFolders::Market().Get(), // "markets"
8451  theReply.m_strServerID.Get(), // "markets/<serverID>"
8452  strMarketDatafile.Get()); // "markets/<serverID>/market_data.bin"
8453  if (!bSuccessStore)
8454  otErr << "Error storing market list to market folder: "
8455  << strMarketDatafile << " \n";
8456 
8457  return true;
8458  }
8459  else if (theReply.m_bSuccess &&
8460  theReply.m_strCommand.Compare("@getMarketOffers")) {
8461  const OTString& strMarketID =
8462  theReply.m_strNymID2; // market ID stored here.
8463 
8464  OTString strOfferDatafile;
8465  strOfferDatafile.Format("%s.bin", strMarketID.Get());
8466 
8467  OTDB::Storage* pStorage = OTDB::GetDefaultStorage();
8468  OT_ASSERT(nullptr != pStorage);
8469 
8470  // The reply is a SUCCESS, and the COUNT is 0 (empty list was returned.)
8471  // Since it was a success, but the list was empty, then we need to erase
8472  // the data file. (So when the file is loaded from storage, it will
8473  // correctly
8474  // display an empty list on the screen, instead of a list of outdated
8475  // items.)
8476  //
8477  if (theReply.m_lDepth == 0) {
8478  bool bSuccessErase = pStorage->EraseValueByKey(
8479  OTFolders::Market().Get(), // "markets"
8480  theReply.m_strServerID.Get(), // "markets/<serverID>offers", //
8481  // "markets/<serverID>/offers"
8482  // // todo stop
8483  // hardcoding.
8484  strOfferDatafile
8485  .Get()); // "markets/<serverID>/offers/<marketID>.bin"
8486  if (!bSuccessErase)
8487  otErr << "Error erasing offers list from market folder: "
8488  << strOfferDatafile << " \n";
8489 
8490  return true;
8491  }
8492 
8493  OTPayload thePayload;
8494 
8495  if ((theReply.m_ascPayload.GetLength() <= 2) ||
8496  (false == theReply.m_ascPayload.GetData(thePayload))) {
8497  otErr << "ProcessServerReply: unable to decode ascii-armored "
8498  "payload in @getMarketOffers reply.\n";
8499  return true;
8500  }
8501 
8502  // Unpack the market list...
8503 
8504  OTDB::OTPacker* pPacker =
8505  pStorage->GetPacker(); // No need to check for failure, since this
8506  // already ASSERTS. No need to cleanup
8507  // either.
8508 
8509  std::unique_ptr<OTDB::PackedBuffer> pBuffer(pPacker->CreateBuffer());
8510  OT_ASSERT(nullptr != pBuffer);
8511 
8512  pBuffer->SetData(
8513  static_cast<const uint8_t*>(thePayload.GetPayloadPointer()),
8514  thePayload.GetSize());
8515 
8516  std::unique_ptr<OTDB::OfferListMarket> pOfferList(
8517  dynamic_cast<OTDB::OfferListMarket*>(
8519 
8520  bool bUnpacked = pPacker->Unpack(*pBuffer, *pOfferList);
8521 
8522  if (!bUnpacked) {
8523  otErr << "Failed unpacking data for process server reply, "
8524  "@getMarketOffers.\n";
8525  return true;
8526  }
8527 
8528  bool bSuccessStore = pStorage->StoreObject(
8529  *pOfferList, OTFolders::Market().Get(), // "markets"
8530  theReply.m_strServerID.Get(), // "markets/<serverID>offers", //
8531  // "markets/<serverID>/offers" //
8532  // todo stop hardcoding.
8533  strOfferDatafile
8534  .Get()); // "markets/<serverID>/offers/<marketID>.bin"
8535  if (!bSuccessStore)
8536  otErr << "Error storing " << strOfferDatafile
8537  << " to market folder.\n";
8538 
8539  return true;
8540  }
8541  else if (theReply.m_bSuccess &&
8542  theReply.m_strCommand.Compare("@getMarketRecentTrades")) {
8543  const OTString& strMarketID =
8544  theReply.m_strNymID2; // market ID stored here.
8545 
8546  OTString strTradeDatafile;
8547  strTradeDatafile.Format("%s.bin", strMarketID.Get());
8548 
8549  OTDB::Storage* pStorage = OTDB::GetDefaultStorage();
8550  OT_ASSERT(nullptr != pStorage);
8551 
8552  // The reply is a SUCCESS, and the COUNT is 0 (empty list was returned.)
8553  // Since it was a success, but the list was empty, then we need to erase
8554  // the data file. (So when the file is loaded from storage, it will
8555  // correctly
8556  // display an empty list on the screen, instead of a list of outdated
8557  // items.)
8558  //
8559  if (theReply.m_lDepth == 0) {
8560  bool bSuccessErase = pStorage->EraseValueByKey(
8561  OTFolders::Market().Get(), // "markets"
8562  theReply.m_strServerID.Get(), // "markets/<serverID>recent", //
8563  // "markets/<serverID>/recent"
8564  // // todo stop
8565  // hardcoding.
8566  strTradeDatafile
8567  .Get()); // "markets/<serverID>/recent/<marketID>.bin"
8568  if (!bSuccessErase)
8569  otErr << "Error erasing recent trades list from market folder: "
8570  << strTradeDatafile << " \n";
8571 
8572  return true;
8573  }
8574 
8575  OTPayload thePayload;
8576 
8577  if ((theReply.m_ascPayload.GetLength() <= 2) ||
8578  (false == theReply.m_ascPayload.GetData(thePayload))) {
8579  otErr << "ProcessServerReply: unable to decode ascii-armored "
8580  "payload in @getMarketRecentTrades reply.\n";
8581  return true;
8582  }
8583 
8584  // Unpack the market list...
8585 
8586  OTDB::OTPacker* pPacker =
8587  pStorage->GetPacker(); // No need to check for failure, since this
8588  // already ASSERTS. No need to cleanup
8589  // either.
8590 
8591  std::unique_ptr<OTDB::PackedBuffer> pBuffer(pPacker->CreateBuffer());
8592  OT_ASSERT(nullptr != pBuffer);
8593 
8594  pBuffer->SetData(
8595  static_cast<const uint8_t*>(thePayload.GetPayloadPointer()),
8596  thePayload.GetSize());
8597 
8598  std::unique_ptr<OTDB::TradeListMarket> pTradeList(
8599  dynamic_cast<OTDB::TradeListMarket*>(
8601 
8602  bool bUnpacked = pPacker->Unpack(*pBuffer, *pTradeList);
8603 
8604  if (!bUnpacked) {
8605  otErr << "Failed unpacking data for process server reply, "
8606  "@getMarketRecentTrades.\n";
8607  return true;
8608  }
8609 
8610  bool bSuccessStore = pStorage->StoreObject(
8611  *pTradeList, OTFolders::Market().Get(), // "markets"
8612  theReply.m_strServerID.Get(), // "markets/<serverID>recent", //
8613  // "markets/<serverID>/recent" //
8614  // todo stop hardcoding.
8615  strTradeDatafile
8616  .Get()); // "markets/<serverID>/recent/<marketID>.bin"
8617  if (!bSuccessStore)
8618  otErr << "Error storing " << strTradeDatafile
8619  << " to market folder.\n";
8620 
8621  return true;
8622  }
8623  else if (theReply.m_bSuccess &&
8624  theReply.m_strCommand.Compare("@getNym_MarketOffers")) {
8625  OTString strOfferDatafile;
8626  strOfferDatafile.Format("%s.bin", theReply.m_strNymID.Get());
8627 
8628  OTDB::Storage* pStorage = OTDB::GetDefaultStorage();
8629  OT_ASSERT(nullptr != pStorage);
8630 
8631  // The reply is a SUCCESS, and the COUNT is 0 (empty list was returned.)
8632  // Since it was a success, but the list was empty, then we need to erase
8633  // the data file. (So when the file is loaded from storage, it will
8634  // correctly
8635  // display an empty list on the screen, instead of a list of outdated
8636  // items.)
8637  //
8638  if (theReply.m_lDepth == 0) {
8639  bool bSuccessErase = pStorage->EraseValueByKey(
8640  OTFolders::Nym().Get(), // "nyms"
8641  theReply.m_strServerID.Get(), // "nyms/<serverID>offers", //
8642  // "nyms/<serverID>/offers" //
8643  // todo stop hardcoding.
8644  strOfferDatafile.Get()); // "nyms/<serverID>/offers/<NymID>.bin"
8645  if (!bSuccessErase)
8646  otErr << "Error erasing offers list from nyms folder: "
8647  << strOfferDatafile << " \n";
8648 
8649  return true;
8650  }
8651 
8652  OTPayload thePayload;
8653 
8654  if ((theReply.m_ascPayload.GetLength() <= 2) ||
8655  (false == theReply.m_ascPayload.GetData(thePayload))) {
8656  otErr << "ProcessServerReply: unable to decode ascii-armored "
8657  "payload in @getNym_MarketOffers reply.\n";
8658  return true;
8659  }
8660 
8661  // Unpack the nym's offer list...
8662 
8663  OTDB::OTPacker* pPacker =
8664  pStorage->GetPacker(); // No need to check for failure, since this
8665  // already ASSERTS. No need to cleanup
8666  // either.
8667 
8668  std::unique_ptr<OTDB::PackedBuffer> pBuffer(pPacker->CreateBuffer());
8669  OT_ASSERT(nullptr != pBuffer);
8670 
8671  pBuffer->SetData(
8672  static_cast<const uint8_t*>(thePayload.GetPayloadPointer()),
8673  thePayload.GetSize());
8674 
8675  std::unique_ptr<OTDB::OfferListNym> pOfferList(
8676  dynamic_cast<OTDB::OfferListNym*>(
8678 
8679  bool bUnpacked = pPacker->Unpack(*pBuffer, *pOfferList);
8680 
8681  if (!bUnpacked) {
8682  otErr << "Failed unpacking data for process server reply, "
8683  "@getNym_MarketOffers.\n";
8684  return true;
8685  }
8686 
8687  bool bSuccessStore = pStorage->StoreObject(
8688  *pOfferList, OTFolders::Nym().Get(), // "nyms"
8689  theReply.m_strServerID.Get(), // "nyms/<serverID>offers", //
8690  // "nyms/<serverID>/offers" // todo
8691  // stop hardcoding.
8692  strOfferDatafile.Get()); // "nyms/<serverID>/offers/<NymID>.bin"
8693  if (!bSuccessStore)
8694  otErr << "Error storing " << strOfferDatafile
8695  << " to nyms folder.\n";
8696 
8697  return true;
8698  }
8699  else if (theReply.m_bSuccess &&
8700  theReply.m_strCommand.Compare("@deleteUserAccount")) {
8701  OTString strOriginalMessage;
8702  if (theReply.m_ascInReferenceTo.Exists())
8703  theReply.m_ascInReferenceTo.GetString(strOriginalMessage);
8704 
8705  OTMessage theOriginalMessage;
8706 
8707  const OTString strServerID(SERVER_ID);
8708 
8709  if (strOriginalMessage.Exists() &&
8710  theOriginalMessage.LoadContractFromString(strOriginalMessage) &&
8711  theOriginalMessage.VerifySignature(*pNym) &&
8712  theOriginalMessage.m_strNymID.Compare(theReply.m_strNymID) &&
8713  theOriginalMessage.m_strCommand.Compare("deleteUserAccount")) {
8714  // O-kayy!!
8715 
8716  while (pNym->GetTransactionNumCount(SERVER_ID) > 0) {
8717  int64_t lTemp =
8718  pNym->GetTransactionNum(SERVER_ID, 0); // index 0
8719  pNym->RemoveTransactionNum(strServerID, lTemp); // doesn't save.
8720  }
8721  while (pNym->GetIssuedNumCount(SERVER_ID) > 0) {
8722  int64_t lTemp = pNym->GetIssuedNum(SERVER_ID, 0); // index 0
8723  pNym->RemoveIssuedNum(strServerID, lTemp); // doesn't save.
8724  }
8725  pNym->UnRegisterAtServer(
8726  strServerID); // Remove request number for that server.
8727 
8728  // SAVE the updated Nym to local storage.
8729  //
8730  OTPseudonym& extraNym = *pNym;
8731  pNym->SaveSignedNymfile(extraNym);
8732 
8733  otOut << "Successfully DELETED Nym from Server: removed request "
8734  "number, plus all issued and transaction numbers for Nym "
8735  << theReply.m_strNymID << " for Server " << strServerID
8736  << ".\n";
8737  }
8738  else
8739  otErr << "The server just for some reason tried to trick me into "
8740  "erasing my issued and transaction numbers for Nym "
8741  << theReply.m_strNymID << ", Server " << strServerID << ".\n";
8742 
8743  return true;
8744  }
8745  else if (theReply.m_bSuccess &&
8746  theReply.m_strCommand.Compare("@deleteAssetAccount")) {
8747  OTString strOriginalMessage;
8748  if (theReply.m_ascInReferenceTo.Exists())
8749  theReply.m_ascInReferenceTo.GetString(strOriginalMessage);
8750 
8751  OTMessage theOriginalMessage;
8752 
8753  const OTString strServerID(SERVER_ID);
8754 
8755  if (strOriginalMessage.Exists() &&
8756  theOriginalMessage.LoadContractFromString(strOriginalMessage) &&
8757  theOriginalMessage.VerifySignature(*pNym) &&
8758  theOriginalMessage.m_strNymID.Compare(theReply.m_strNymID) &&
8759  theOriginalMessage.m_strAcctID.Compare(theReply.m_strAcctID) &&
8760  theOriginalMessage.m_strCommand.Compare("deleteAssetAccount")) {
8761  // O-kayy!!
8762 
8763  const OTIdentifier theAccountID(theReply.m_strAcctID);
8764 
8765  OTAccount* pDeletedAcct = m_pWallet->GetAccount(theAccountID);
8766 
8767  if (nullptr != pDeletedAcct) {
8768  pDeletedAcct->MarkForDeletion();
8769  pDeletedAcct->ReleaseSignatures();
8770  pDeletedAcct->SignContract(*pNym);
8771  pDeletedAcct->SaveContract();
8772  pDeletedAcct->SaveAccount();
8773  // (The account still exists in storage, but has been MARKED FOR
8774  // DELETION.)
8775 
8776  // Remove the account from the wallet:
8777  //
8778  if (m_pWallet->RemoveAccount(theAccountID)) {
8779  m_pWallet->SaveWallet();
8780  }
8781  }
8782 
8783  otOut << "Successfully DELETED Asset Acct " << theReply.m_strAcctID
8784  << " from Server: " << strServerID << ".\n";
8785  }
8786  else
8787  otErr << "The server just for some reason tried to trick me into "
8788  "erasing my account " << theReply.m_strAcctID
8789  << " on Server " << strServerID << ".\n";
8790 
8791  return true;
8792  }
8793  else if (theReply.m_bSuccess &&
8794  theReply.m_strCommand.Compare("@issueAssetType")) {
8795  if (theReply.m_ascPayload.GetLength()) {
8796  OTAccount* pAccount = nullptr;
8797 
8798  // this decodes the ascii-armor payload where the new account file
8799  // is stored, and returns a normal string in strAcctContents.
8800  OTString strAcctContents(theReply.m_ascPayload);
8801 
8802  // TODO check return value
8803  pAccount = new OTAccount(USER_ID, ACCOUNT_ID, SERVER_ID);
8804 
8805  if (pAccount->LoadContractFromString(strAcctContents) &&
8806  pAccount->VerifyAccount(*pServerNym)) {
8807  // (2) Sign the Account
8808  pAccount->SignContract(*pNym);
8809  pAccount->SaveContract();
8810 
8811  // (3) Save the Account to file
8812  pAccount->SaveAccount();
8813 
8814  // Need to consider other security considerations.
8815  // What if I wasn't EXPECTING a @issueAssetType message?
8816  // Well actually, in that case, the server wouldn't have a
8817  // copy of my request to send back to me, would he? So I should
8818  // check that request to make sure it's good.
8819  // Also maybe should check to see if I was expecting this
8820  // message
8821  // in the first place.
8822 
8823  m_pWallet->AddAccount(*pAccount);
8824  m_pWallet->SaveWallet();
8825 
8826  return true;
8827  }
8828  else {
8829  delete pAccount;
8830  pAccount = nullptr;
8831  }
8832  }
8833  }
8834  else if (theReply.m_bSuccess &&
8835  theReply.m_strCommand.Compare("@createAccount")) {
8836  if (theReply.m_ascPayload.GetLength()) {
8837  OTAccount* pAccount = nullptr;
8838 
8839  // this decodes the ascii-armor payload where the new account file
8840  // is stored, and returns a normal string in strAcctContents.
8841  OTString strAcctContents(theReply.m_ascPayload);
8842 
8843  pAccount = new OTAccount(USER_ID, ACCOUNT_ID, SERVER_ID);
8844 
8845  if (pAccount && pAccount->LoadContractFromString(strAcctContents) &&
8846  pAccount->VerifyAccount(*pServerNym)) {
8847  // (2) Sign the Account
8848  pAccount->ReleaseSignatures(); // So I don't get the annoying
8849  // failure to verify message from
8850  // the server's signature.
8851  // Will eventually end up keeping the signature, however, just
8852  // for reasons of proof.
8853  // UPDATE (above) we are releasing these now, for good, since
8854  // server's signature is not needed. Receipts are functional
8855  // now,
8856  pAccount->SignContract(*pNym); // and the last receipt IS signed
8857  // by the server, and it can be
8858  // used to verify the nym,
8859  // account, inbox, and outbox.
8860  // Nifty!
8861  pAccount->SaveContract();
8862 
8863  // (3) Save the Account to file
8864  pAccount->SaveAccount();
8865 
8866  // Need to consider other security considerations.
8867  // What if I wasn't EXPECTING a @createAccount message?
8868  // Well actually, in that case, the server wouldn't have a
8869  // copy of my request to send back to me, would he? So I should
8870  // check that request to make sure it's good.
8871  // Also maybe should check to see if I was expecting this
8872  // message
8873  // in the first place.
8874 
8875  m_pWallet->AddAccount(*pAccount);
8876  m_pWallet->SaveWallet();
8877 
8878  return true;
8879  }
8880  else {
8881  delete pAccount;
8882  pAccount = nullptr;
8883  }
8884  }
8885  }
8886  else {
8887  }
8888  return false;
8889 }
8890 
8897 //
8906  OTClient::OT_CLIENT_CMD_TYPE requestedCommand, OTMessage& theMessage,
8907  OTPseudonym& theNym,
8908  // OTAssetContract& theContract,
8909  const OTServerContract& theServer, const OTAccount* pAccount,
8910  int64_t lTransactionAmount, OTAssetContract* pMyAssetContract,
8911  const OTIdentifier* pHisNymID, const OTIdentifier* pHisAcctID)
8912 {
8913  // This is all preparatory work to get the various pieces of data together
8914  // -- only
8915  // then can we put those pieces into a message.
8916  OTIdentifier CONTRACT_ID;
8917  OTString strNymID, strContractID, strServerID, strNymPublicKey,
8918  strAccountID;
8919  int64_t lRequestNumber = 0;
8920 
8921  theNym.GetIdentifier(strNymID);
8922  theServer.GetIdentifier(strServerID);
8923 
8924  const OTIdentifier SERVER_ID(strServerID);
8925 
8926  if (nullptr != pAccount) {
8927  pAccount->GetIdentifier(strAccountID);
8928 
8929  if (pAccount->GetPurportedServerID() != SERVER_ID) {
8930  otErr << "OTClient::ProcessUserCommand: "
8931  "pAccount->GetPurportedServerID() doesn't match "
8932  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
8933  return CalcReturnVal(-1);
8934  ;
8935  }
8936  }
8937 
8938  bool bSendCommand = false;
8939  int64_t lReturnValue = 0;
8940 
8941  // THE BIG SWITCH (inside a code block for neatness
8942  switch (requestedCommand) {
8943 
8944  case (OTClient::checkServerID): {
8945  OTString strAuthentKey, strEncryptionKey;
8946 
8947  theNym.GetPublicAuthKey().GetPublicKey(strAuthentKey);
8948  theNym.GetPublicEncrKey().GetPublicKey(strEncryptionKey);
8949 
8950  // (1) set up member variables
8951  theMessage.m_strCommand = "checkServerID";
8952  theMessage.m_strNymID = strNymID; // Not expected to verify in any way
8953  // (for this message.) Just mirrored
8954  // back in the reply.
8955  theMessage.m_strServerID = strServerID;
8956  theMessage.m_strNymPublicKey =
8957  strAuthentKey; // Authentication public key for this Nym. (That he's
8958  // signing this message with...)
8959  theMessage.m_strNymID2 = strEncryptionKey; // Encryption public key for
8960  // this Nym (to send an
8961  // encrypted reply back.)
8962 
8963  theMessage.m_strRequestNum.Format(
8964  "%d", 1); // Request Number, if unused, should be set to 1.
8965 
8966  // (2) Sign the Message
8967  // When a message is signed, it updates its m_xmlUnsigned contents to
8968  // the values in the member variables
8969  theMessage.SignContract(theNym);
8970 
8971  // (3) Save the Message (with signatures and all, back to its internal
8972  // member m_strRawFile.)
8973  //
8974  // FYI, SaveContract takes m_xmlUnsigned and wraps it with the
8975  // signatures and ------- BEGIN bookends
8976  // If you don't pass a string in, then SaveContract saves the new
8977  // version to its member, m_strRawFile
8978  theMessage.SaveContract();
8979 
8980  bSendCommand = true;
8981  lReturnValue = 1;
8982 
8983  } break;
8984 
8985  case (OTClient::createUserAccount): {
8986  // Create a new OTDB::StringMap object.
8987  //
8988  std::unique_ptr<OTDB::Storable> pStorable(OTDB::CreateObject(
8989  OTDB::STORED_OBJ_STRING_MAP)); // this asserts already, on failure.
8990  OTDB::StringMap* pMap = dynamic_cast<OTDB::StringMap*>(pStorable.get());
8991  if (nullptr == pMap)
8992  otErr << __FUNCTION__ << ": Error: failed trying to load or create "
8993  "a STORED_OBJ_STRING_MAP.\n";
8994  else // It instantiated.
8995  {
8996  OTString strCredList;
8997  OTString::Map& theMap = pMap->the_map;
8998 
8999  // Credentials exist already.
9000  if (theNym.GetMasterCredentialCount() > 0) {
9001  theNym.GetPublicCredentials(strCredList, &theMap);
9002  }
9003  else // No credentials? Create them, then.
9004  {
9005  OTString strMasterCredID;
9006  const bool bAddedMaster =
9007  theNym.AddNewMasterCredential(strMasterCredID);
9008 
9009  if (bAddedMaster && strMasterCredID.Exists() &&
9010  (theNym.GetMasterCredentialCount() > 0)) {
9011  otOut << __FUNCTION__
9012  << ": Adding new keyCredential to master credential: "
9013  << strMasterCredID << "\n";
9014 
9015  const OTIdentifier theMasterCredID(strMasterCredID);
9016 
9017  const bool bAddedSubkey =
9018  theNym.AddNewSubkey(theMasterCredID);
9019 
9020  if (bAddedSubkey) {
9021  theNym.SaveCredentialList();
9022  theNym.GetPublicCredentials(strCredList, &theMap);
9023  }
9024  else
9025  otErr << __FUNCTION__ << ": Failed trying to add new "
9026  "keyCredential to new Master "
9027  "credential.\n";
9028  }
9029  else
9030  otErr << __FUNCTION__ << ": Failed trying to add new "
9031  "master credential (for Nym who "
9032  "doesn't have one yet.)\n";
9033  }
9034  // Serialize the StringMap to a string...
9035 
9036  // Won't bother if there are zero credentials somehow.
9037  if (strCredList.Exists() && (!theMap.empty())) {
9038  std::string str_Encoded = OTDB::EncodeObject(*pMap);
9039  const bool bSuccessEncoding = (str_Encoded.size() > 0);
9040  if (bSuccessEncoding) {
9041  theMessage.m_ascPayload.SetString(
9042  strCredList); // <========== Success
9043  theMessage.m_ascPayload2.Set(
9044  str_Encoded.c_str()); // Payload contains credentials
9045  // list, payload2 contains actual
9046  // credentials.
9047  }
9048  }
9049  }
9050  if (!theMessage.m_ascPayload.Exists() ||
9051  !theMessage.m_ascPayload2.Exists()) {
9052  otErr << __FUNCTION__ << ": Failed trying to assemble a "
9053  "createUserAccount message: This Nym has "
9054  "no credentials to use for registration. "
9055  "Convert this Nym first to the new "
9056  "credential system, then try again.\n";
9057  }
9058  else {
9059  // (1) set up member variables
9060  theMessage.m_strCommand = "createUserAccount";
9061  theMessage.m_strNymID = strNymID;
9062  theMessage.m_strServerID = strServerID;
9063 
9064  // theNym.GetPublicKey().GetPublicKey(strNymPublicKey);
9065  // theMessage.m_strNymPublicKey = strNymPublicKey; //
9066  // Deprecated. (Credentials are new.)
9067 
9068  // THIS APPEARS SLIGHTLY ABOVE. Just leaving as a comment
9069  // here so it's not forgotten that this is also happening.
9070  //
9071  // theMessage.m_ascPayload.SetString(strCredList); //
9072  // <========== Success
9073  // theMessage.m_ascPayload2.Set(str_Encoded.c_str()); //
9074  // Payload contains credentials list, payload2 contains actual
9075  // credentials.
9076 
9077  theMessage.m_strRequestNum.Format(
9078  "%d", 1); // Request Number, if unused, should be set to 1.
9079 
9080  // (2) Sign the Message
9081  theMessage.SignContract(theNym);
9082 
9083  // (3) Save the Message (with signatures and all, back to its
9084  // internal member m_strRawFile.)
9085  theMessage.SaveContract();
9086 
9087  bSendCommand = true;
9088  lReturnValue = 1;
9089  }
9090  } break;
9091  case (OTClient::getRequest): {
9092  // otOut << "(User has instructed to send a getRequest command to
9093  // the server...)\n";
9094 
9095  // (1) set up member variables
9096  theMessage.m_strCommand = "getRequest";
9097  theMessage.m_strNymID = strNymID;
9098  theMessage.m_strServerID = strServerID;
9099 
9100  theMessage.m_strRequestNum.Format(
9101  "%d", 1); // Request Number, if unused, should be set to 1.
9102 
9103  // (2) Sign the Message
9104  theMessage.SignContract(theNym);
9105 
9106  // (3) Save the Message (with signatures and all, back to its internal
9107  // member m_strRawFile.)
9108  theMessage.SaveContract();
9109 
9110  bSendCommand = true;
9111  lReturnValue = 1;
9112  }
9113 
9114  // EVERY COMMAND BELOW THIS POINT (THEY ARE ALL OUTGOING TO THE SERVER) MUST
9115  // INCLUDE THE
9116  // CORRECT REQUEST NUMBER, OR BE REJECTED BY THE SERVER.
9117  //
9118  // The same commands must also increment the local counter of the request
9119  // number by calling theNym.IncrementRequestNum
9120  // Otherwise it will get out of sync, and future commands will start failing
9121  // (until it is resynchronized with
9122  // a getRequest message to the server, which replies with the latest number.
9123  // The code on this side that processes
9124  // that server reply is already smart enough to update the local nym's copy
9125  // of the request number when it is received.
9126  // In this way, the client becomes resynchronized and the next command will
9127  // work again. But it's better to increment the
9128  // counter properly.
9129  // PROPERLY == every time you actually get the request number from a nym and
9130  // use it to make a server request,
9131  // then you should therefore also increment that counter. If you call
9132  // GetCurrentRequestNum AND USE IT WITH THE SERVER,
9133  // then make sure you call IncrementRequestNum immediately after. Otherwise
9134  // future commands will start failing.
9135  //
9136  // This is all because the server requres a new request number (last one +1)
9137  // with each request. This is in
9138  // order to thwart would-be attackers who cannot break the crypto, but try
9139  // to capture encrypted messages and
9140  // send them to the server twice. Better that new requests requre new
9141  // request numbers :-)
9142  break;
9144  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
9145  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
9146  theMessage.m_strRequestNum.Format(
9147  "%lld", lRequestNumber); // Always have to send this.
9148  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
9149  // a server request, I
9150  // have to increment it
9151 
9152  // otOut << "(User has instructed to send a deleteUserAccount
9153  // command to the server...)\n";
9154 
9155  // (1) set up member variables
9156  theMessage.m_strCommand = "deleteUserAccount";
9157  theMessage.m_strNymID = strNymID;
9158  theMessage.m_strServerID = strServerID;
9159  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
9160  // theMessage.m_strServerID is
9161  // already set. (It uses it.)
9162 
9163  // (2) Sign the Message
9164  theMessage.SignContract(theNym);
9165 
9166  // (3) Save the Message (with signatures and all, back to its internal
9167  // member m_strRawFile.)
9168  theMessage.SaveContract();
9169 
9170  bSendCommand = true;
9171  lReturnValue = lRequestNumber;
9172  } break;
9173  case OTClient::sendUserMessage: // SEND USER MESSAGE
9174  {
9175  otOut << "Please enter a NymID (for recipient): ";
9176 
9177  // User input.
9178  // I need a second NymID, so I allow the user to enter it here.
9179  OTString strNymID2;
9180  strNymID2.OTfgets(std::cin);
9181 
9182  otOut << "Enter recipient's public key:\n> ";
9183 
9184  OTASCIIArmor theArmoredText;
9185  char decode_buffer[200]; // Safe since we only read sizeof - 1.
9186 
9187  do {
9188  decode_buffer[0] = 0;
9189 
9190  if (!fgets(decode_buffer, sizeof(decode_buffer) - 1, stdin)) {
9191  theArmoredText.Concatenate("%s\n", decode_buffer);
9192  otOut << "> ";
9193  }
9194  else {
9195  break;
9196  }
9197 
9198  } while (strlen(decode_buffer) > 1);
9199 
9200  decode_buffer[0] = '\0';
9201  OTString strPlaintext;
9202 
9203  otOut << "Please enter a plaintext message, terminate with ~ on a new "
9204  "line:\n> ";
9205 
9206  do {
9207  if (!fgets(decode_buffer, sizeof(decode_buffer), stdin) &&
9208  decode_buffer[0] != '~') {
9209  strPlaintext.Concatenate("%s", decode_buffer);
9210  otOut << "> ";
9211  }
9212  } while (decode_buffer[0] != '~');
9213 
9214  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
9215  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
9216  theMessage.m_strRequestNum.Format(
9217  "%lld", lRequestNumber); // Always have to send this.
9218  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
9219  // a server request, I
9220  // have to increment it
9221 
9222  // (1) set up member variables
9223  theMessage.m_strCommand = "sendUserMessage";
9224  theMessage.m_strNymID = strNymID;
9225  theMessage.m_strNymID2 = strNymID2;
9226  theMessage.m_strServerID = strServerID;
9227  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
9228  // theMessage.m_strServerID is
9229  // already set. (It uses it.)
9230 
9231  OTEnvelope theEnvelope;
9232  std::unique_ptr<OTAsymmetricKey> pPubkey(OTAsymmetricKey::KeyFactory());
9233  OT_ASSERT(nullptr != pPubkey);
9234 
9235  if (theArmoredText.Exists() && !pPubkey->SetPublicKey(theArmoredText)) {
9236  otOut << "Failed setting public key.\n";
9237  }
9238  else if (strPlaintext.Exists() &&
9239  theEnvelope.Seal(*pPubkey, strPlaintext) &&
9240  theEnvelope.GetAsciiArmoredData(theMessage.m_ascPayload)) {
9241  // (2) Sign the Message
9242  theMessage.SignContract(theNym);
9243 
9244  // (3) Save the Message (with signatures and all, back to its
9245  // internal member m_strRawFile.)
9246  theMessage.SaveContract();
9247 
9248  // (Send it)
9249  bSendCommand = true;
9250  lReturnValue = lRequestNumber;
9251 
9252  // store a copy in the outmail.
9253  // (not encrypted, since the Nymfile will be encrypted anyway.
9254  //
9255  OTMessage* pMessage = new OTMessage;
9256 
9257  OT_ASSERT(nullptr != pMessage);
9258 
9259  pMessage->m_strCommand = "outmailMessage";
9260  pMessage->m_strNymID = strNymID;
9261  pMessage->m_strNymID2 = strNymID2;
9262  pMessage->m_strServerID = strServerID;
9263  pMessage->m_strRequestNum.Format("%lld", lRequestNumber);
9264 
9265  pMessage->SetAcknowledgments(theNym); // Must be called AFTER
9266  // theMessage.m_strServerID is
9267  // already set. (It uses it.)
9268 
9269  pMessage->m_ascPayload.SetString(strPlaintext);
9270 
9271  pMessage->SignContract(theNym);
9272  pMessage->SaveContract();
9273 
9274  theNym.AddOutmail(*pMessage); // Now the Nym is responsible to
9275  // delete it. It's in his "outmail".
9276 
9277  OTPseudonym& extraNym = theNym;
9278  theNym.SaveSignedNymfile(extraNym);
9279  }
9280  else {
9281  otOut << "Failed sealing envelope.\n";
9282  }
9283  } break;
9284  case OTClient::checkUser: // CHECK USER
9285  {
9286  otOut << "Please enter a NymID: ";
9287 
9288  // User input.
9289  // I need a second NymID, so I allow the user to enter it here.
9290  OTString strNymID2;
9291  strNymID2.OTfgets(std::cin);
9292 
9293  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
9294  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
9295  theMessage.m_strRequestNum.Format(
9296  "%lld", lRequestNumber); // Always have to send this.
9297  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
9298  // a server request, I
9299  // have to increment it
9300 
9301  // (1) set up member variables
9302  theMessage.m_strCommand = "checkUser";
9303  theMessage.m_strNymID = strNymID;
9304  theMessage.m_strNymID2 = strNymID2;
9305  theMessage.m_strServerID = strServerID;
9306  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
9307  // theMessage.m_strServerID is
9308  // already set. (It uses it.)
9309 
9310  // (2) Sign the Message
9311  theMessage.SignContract(theNym);
9312 
9313  // (3) Save the Message (with signatures and all, back to its internal
9314  // member m_strRawFile.)
9315  theMessage.SaveContract();
9316 
9317  bSendCommand = true;
9318  lReturnValue = lRequestNumber;
9319  }
9320 
9321  // The standard "contract" key inside the new currency contract must be the
9322  // same key
9323  // used by the Nym who is signing the requests to issue the currency.
9324  break;
9325  case OTClient::issueAssetType: // ISSUE ASSET TYPE
9326  {
9327  OTString strSourceContract;
9328 
9329  otOut << "Please enter currency contract, terminate with ~ on a new "
9330  "line:\n> ";
9331  char decode_buffer[200];
9332 
9333  do {
9334  if (!fgets(decode_buffer, sizeof(decode_buffer), stdin) &&
9335  decode_buffer[0] != '~') {
9336  strSourceContract.Concatenate("%s", decode_buffer);
9337  otOut << "> ";
9338  }
9339  } while (decode_buffer[0] != '~');
9340 
9341  /*
9342  // While debugging, sometimes I just want it to read the source contract
9343  directly out of a test file.
9344  // So I use this code, instead of the above code, when I am doing that,
9345  to set strSourceContract's value...
9346  //
9347 
9348  OTString strTempPath;
9349  strTempPath.Format("%s%s%s%s%s", OTLog::Path(), OTLog::PathSeparator(),
9350  "sample-contract", OTLog::PathSeparator(), "tokens.xml");
9351  std::ifstream in(strTempPath.Get(), std::ios::binary);
9352 
9353  std::ifstream in(strTempPath.Get(), std::ios::binary);
9354 
9355  if (in.fail())
9356  {
9357  otErr << "Error opening file WHILE DEBUGGING: " << strTempPath << "\n";
9358  }
9359  OT_ASSERT(!in.fail());
9360 
9361  std::stringstream buffer;
9362  buffer << in.rdbuf();
9363 
9364  std::string contents(buffer.str());
9365 
9366  strSourceContract = contents.c_str();
9367  */
9368 
9369  OTAssetContract theAssetContract;
9370 
9371  if (theAssetContract.LoadContractFromString(strSourceContract)) {
9372  // In some places the ID is already set, and I'd verify it here.
9373  // But in this place, I am adding it and generating the ID from the
9374  // string.
9375  OTIdentifier newID;
9376  theAssetContract.CalculateContractID(newID);
9377  theAssetContract.SetIdentifier(newID); // probably unnecessary
9378 
9379  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
9380  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
9381  theMessage.m_strRequestNum.Format(
9382  "%lld", lRequestNumber); // Always have to send this.
9383  theNym.IncrementRequestNum(theNym, strServerID); // since I used it
9384  // for a server
9385  // request, I have
9386  // to increment it
9387 
9388  // (1) Set up member variables
9389  theMessage.m_strCommand = "issueAssetType";
9390  theMessage.m_strNymID = strNymID;
9391  theMessage.m_strServerID = strServerID;
9392  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
9393  // theMessage.m_strServerID
9394  // is already set. (It uses
9395  // it.)
9396 
9397  newID.GetString(theMessage.m_strAssetID); // I've calculated the ID,
9398  // and now put it on the
9399  // message...
9400  OTString strAssetContract(theAssetContract);
9401  theMessage.m_ascPayload.SetString(strAssetContract);
9402 
9403  // (2) Sign the Message
9404  theMessage.SignContract(theNym);
9405 
9406  // (3) Save the Message (with signatures and all, back to its
9407  // internal member m_strRawFile.)
9408  theMessage.SaveContract();
9409 
9410  bSendCommand = true;
9411  lReturnValue = lRequestNumber;
9412 
9413  // Save the contract to local storage and add to wallet.
9414 
9415  OTString strFoldername(OTFolders::Contract().Get());
9416  OTString strFilename(
9417  theMessage.m_strAssetID.Get()); // In this case the filename
9418  // isn't actually used, since
9419  // SaveToContractFolder will
9420  // handle setting up the filename and overwrite it anyway. But I
9421  // still prefer to set it
9422  // up correctly, rather than pass a blank. I'm just funny like that.
9423 
9424  OTAssetContract* pContract =
9425  new OTAssetContract(theMessage.m_strAssetID, strFoldername,
9426  strFilename, theMessage.m_strAssetID);
9427  OT_ASSERT(nullptr != pContract);
9428 
9429  // Check the server signature on the contract here. (Perhaps the
9430  // message is good enough?
9431  // After all, the message IS signed by the server and contains the
9432  // Account.
9433  // if (pContract->LoadContract() &&
9434  // pContract->VerifyContract())
9435  if (pContract->LoadContractFromString(strSourceContract) &&
9436  pContract->VerifyContract()) {
9437  // Next make sure the wallet has this contract on its list...
9438  OTWallet* pWallet = nullptr;
9439 
9440  if (nullptr != (pWallet = m_pWallet)) {
9441  pWallet->AddAssetContract(*pContract); // this saves both
9442  // the contract and
9443  // the wallet.
9444  pContract =
9445  nullptr; // Success. The wallet "owns" it now, no
9446  // need to clean it up.
9447  }
9448  }
9449  // cleanup
9450  if (pContract) {
9451  delete pContract;
9452  pContract = nullptr;
9453  }
9454  }
9455  }
9456 
9457  // DONE: This will have to be changed from a simple message, to a
9458  // transaction,
9459  // BECAUSE IT CHANGES ACCOUNT BALANCES, and thus requires balance agreement
9460  // for all affected accounts!
9461  break;
9462  case OTClient::exchangeBasket: // EXCHANGE BASKET
9463  {
9464  const OTIdentifier USER_ID(theNym);
9465 
9466  OTString strBasketInfo, strNymID(USER_ID);
9467  OTString str_BASKET_CONTRACT_ID, str_MAIN_ACCOUNT_ID, strTemp;
9468 
9469  // FIRST get the Asset Type ID for the basket
9470  otOut << "Enter the basket's Asset Type ID (aka Contract ID): ";
9471  str_BASKET_CONTRACT_ID.OTfgets(std::cin);
9472 
9473  // FIRST get the Asset Type ID for the basket
9474  otOut << "Enter an ACCOUNT ID of yours for an account that has the "
9475  "same asset type: ";
9476  str_MAIN_ACCOUNT_ID.OTfgets(std::cin);
9477  OTIdentifier MAIN_ACCOUNT_ID(str_MAIN_ACCOUNT_ID);
9478 
9479  // which direction is the exchange?
9480  OTString strDirection;
9481  otOut << "Are you exchanging in or out? [in]: ";
9482  strDirection.OTfgets(std::cin);
9483 
9484  const bool bDirection =
9485  (strDirection.Compare("in") || strDirection.Compare("In"));
9486 
9487  // load up the asset contract
9488  OTString strFoldername(OTFolders::Contract().Get());
9489  OTString strContractPath(str_BASKET_CONTRACT_ID.Get());
9490  std::unique_ptr<OTAssetContract> pContract(
9491  new OTAssetContract(str_BASKET_CONTRACT_ID, strFoldername,
9492  strContractPath, str_BASKET_CONTRACT_ID));
9493  OT_ASSERT(nullptr != pContract);
9494 
9495  if (pContract->LoadContract() && pContract->VerifyContract()) {
9496  // Next load the Basket object out of that contract.
9497  Basket theBasket;
9498 
9499  // todo perhaps verify the basket here, even though I just verified
9500  // the asset contract itself...
9501  // Can't never be too sure.
9502  if (pContract->GetBasketInfo().GetLength() &&
9503  theBasket.LoadContractFromString(pContract->GetBasketInfo())) {
9504  // We need a transaction number just to send this thing. Plus,
9505  // we need a number for
9506  // each sub-account to the basket, as well as the basket's main
9507  // account.
9508  // That is: 1 + theBasket.Count() + 1
9509 
9510  if (theNym.GetTransactionNumCount(SERVER_ID) <
9511  (2 + theBasket.Count())) {
9512  otOut << __FUNCTION__ << ": Trying to exchange basket: you "
9513  "don't have enough transaction "
9514  "numbers to perform the "
9515  "exchange.\n";
9516  }
9517  else {
9518  int64_t lStoredTransactionNumber = 0;
9519  bool bGotTransNum = theNym.GetNextTransactionNum(
9520  theNym, strServerID,
9521  lStoredTransactionNumber); // this saves
9522 
9523  if (bGotTransNum) {
9524  // Create a transaction
9525  OTTransaction* pTransaction =
9527  USER_ID, MAIN_ACCOUNT_ID, SERVER_ID,
9529  lStoredTransactionNumber);
9530 
9531  // set up the transaction item (each transaction may
9532  // have multiple items...)
9534  *pTransaction, OTItem::exchangeBasket);
9535 
9536  // This pItem is where the Basket Info will be stored.
9537  // (So it ends up on receipts...)
9538  pTransaction->AddItem(*pItem); // the Transaction's
9539  // destructor will
9540  // cleanup the item. It
9541  // "owns" it now.
9542 
9543  // GET / LOAD the ACCOUNT / INBOX /OUTBOX for the MAIN
9544  // ACCOUNT
9545 
9546  OTAccount* pMainAccount =
9547  m_pWallet->GetAccount(MAIN_ACCOUNT_ID);
9548  OT_ASSERT(nullptr != pMainAccount); // todo. better than
9549  // nothing for now.
9550 
9551  std::unique_ptr<OTLedger> pInbox(
9552  pMainAccount->LoadInbox(theNym));
9553  std::unique_ptr<OTLedger> pOutbox(
9554  pMainAccount->LoadOutbox(theNym));
9555 
9556  if (nullptr == pInbox) {
9557  otOut << "Failed loading inbox!\n";
9558 
9559  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF
9560  // AVAILABLE NUMBERS.
9561  theNym.AddTransactionNum(theNym, strServerID,
9562  lStoredTransactionNumber,
9563  true); // bSave=true
9564  }
9565  else if (nullptr == pOutbox) {
9566  otOut << "Failed loading outbox!\n";
9567 
9568  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF
9569  // AVAILABLE NUMBERS.
9570  theNym.AddTransactionNum(theNym, strServerID,
9571  lStoredTransactionNumber,
9572  true); // bSave=true
9573  }
9574  // Set up the Request Basket!
9575  else {
9576  int32_t nTransferMultiple = 0;
9577 
9578  Basket theRequestBasket(
9579  theBasket.Count(),
9580  theBasket.GetMinimumTransfer());
9581 
9582  theRequestBasket.SetExchangingIn(bDirection);
9583  // Show the minimum transfer amount to the customer
9584  // and ask him to choose a multiple for the transfer
9585  otOut << "The minimum transfer amount for this "
9586  "basket is "
9587  << theBasket.GetMinimumTransfer()
9588  << ". You may only exchange in multiples of "
9589  "it.\nChoose any multiple [1]: ";
9590  strTemp.OTfgets(std::cin);
9591  nTransferMultiple = atoi(strTemp.Get());
9592  strTemp.Release();
9593  if (nTransferMultiple <= 0) nTransferMultiple = 1;
9594 
9595  theRequestBasket.SetTransferMultiple(
9596  nTransferMultiple);
9597 
9598  // NOTE: I'm not checking this call for success...
9599  // But, I DID check the count beforehand, and I know
9600  // there are enough numbers.
9601  //
9602  int64_t lClosingTransactionNo = 0;
9603  theNym.GetNextTransactionNum(
9604  theNym, strServerID,
9605  lClosingTransactionNo); // this saves
9606 
9607  theRequestBasket.SetClosingNum(
9608  lClosingTransactionNo); // For the basketReceipt
9609  // (Closing Transaction
9610  // Num) for main
9611  // account.
9612 
9613  // Then loop through the BasketItems...
9614  for (int32_t i = 0; i < theBasket.Count(); i++) {
9615  // pItem-> contains SUB_CONTRACT_ID,
9616  // SUB_ACCOUNT_ID, and lMinimumTransferAmount.
9617  BasketItem* pItem = theBasket.At(i);
9618 
9619  // ...and for each one, ask the user to enter
9620  // his corresponding account ID.
9621  // IT MUST BE THE SAME ASSET TYPE AS THE BASKET
9622  // ITEM, SO SHOW USER THE ASSET ID!
9623  OTString str_SUB_CONTRACT_ID(
9624  pItem->SUB_CONTRACT_ID);
9625  otOut << "\nBasket currency type (Asset Type) #"
9626  << (i + 1) << " is:\n"
9627  << str_SUB_CONTRACT_ID
9628  << "\n\nPlease enter your own existing "
9629  "Account ID of the same asset type: ";
9630  OTString str_TEMP_ACCOUNT_ID;
9631  str_TEMP_ACCOUNT_ID.OTfgets(std::cin);
9632  OTIdentifier TEMP_ACCOUNT_ID(
9633  str_TEMP_ACCOUNT_ID);
9634 
9635  // TODO (later) Load up the user's account at
9636  // this point to make sure it even exists.
9637  // Then check the asset type on that account
9638  // against pItem->SUB_CONTRACT_ID and make sure
9639  // they match.
9640  // The server will definitely have to do this
9641  // anyway. The client can skip it, but the
9642  // command
9643  // won't work unless the user enters this data
9644  // properly. UI will have to do a popup here if
9645  // something is wrong.
9646 
9647  // As this is happening, we're creating ANOTHER
9648  // basket object (theRequestBasket), with items
9649  // that
9650  // contain MY account IDs.
9651  // The minimum transfer amounts on the new
9652  // basket are set based on a multiple of the
9653  // original.
9654  // (I already set the multiple just above this
9655  // loop.)
9656 
9657  // NOTE: I'm not checking this call for
9658  // success...
9659  // But, I DID check the count beforehand, and I
9660  // know there are enough numbers.
9661  //
9662  int64_t lSubClosingTransactionNo =
9663  0; // For the basketReceipt (closing
9664  // transaction num) for the sub account.
9665  theNym.GetNextTransactionNum(
9666  theNym, strServerID,
9667  lSubClosingTransactionNo); // this saves
9668  theRequestBasket.AddRequestSubContract(
9669  pItem->SUB_CONTRACT_ID, TEMP_ACCOUNT_ID,
9670  lSubClosingTransactionNo);
9671  }
9672 
9673  // Make sure the server knows where to put my new
9674  // basket currency once the exchange is done.
9675  theRequestBasket.SetRequestAccountID(
9676  MAIN_ACCOUNT_ID);
9677 
9678  // Export the Basket object into a string, add it
9679  // as
9680  // a payload on my request, and send to server.
9681  theRequestBasket.SignContract(theNym);
9682  theRequestBasket.SaveContractRaw(strBasketInfo);
9683 
9684  pItem->SetAttachment(strBasketInfo);
9685 
9686  // sign the item. save it.
9687  //
9688  pItem->SignContract(theNym);
9689  pItem->SaveContract();
9690 
9691  // BALANCE AGREEMENT!
9692  //
9693  // pBalanceItem is signed and saved within this
9694  // call. No need to do that again.
9695  OTItem* pBalanceItem =
9696  pInbox->GenerateBalanceStatement(
9697  0, // Change in balance is 0. (The accounts
9698  // will all be changed,
9699  *pTransaction, theNym, *pMainAccount,
9700  *pOutbox); // but basketReceipts will be
9701  // used to account for it.)
9702 
9703  if (nullptr !=
9704  pBalanceItem) // will never be nullptr.
9705  // Will assert above
9706  // before it gets here.
9707  pTransaction->AddItem(
9708  *pBalanceItem); // Better not be nullptr...
9709  // message will fail... But
9710  // better check anyway.
9711 
9712  // sign the transaction
9713  pTransaction->SignContract(theNym);
9714  pTransaction->SaveContract();
9715 
9716  // set up the ledger
9717  OTLedger theLedger(USER_ID, MAIN_ACCOUNT_ID,
9718  SERVER_ID);
9719  theLedger.GenerateLedger(
9720  MAIN_ACCOUNT_ID, SERVER_ID,
9721  OTLedger::message); // bGenerateLedger defaults
9722  // to false, which is
9723  // correct.
9724  theLedger.AddTransaction(*pTransaction);
9725 
9726  // sign the ledger
9727  theLedger.SignContract(theNym);
9728  theLedger.SaveContract();
9729 
9730  // extract the ledger in ascii-armored form
9731  OTString strLedger(theLedger);
9732  OTASCIIArmor ascLedger; // I can't pass strLedger
9733  // into this constructor
9734  // because I want to encode
9735  // it
9736 
9737  // Encoding...
9738  ascLedger.SetString(strLedger);
9739 
9740  // (0) Set up the REQUEST NUMBER and then INCREMENT
9741  // IT
9742  theNym.GetCurrentRequestNum(strServerID,
9743  lRequestNumber);
9744  theMessage.m_strRequestNum.Format(
9745  "%lld",
9746  lRequestNumber); // Always have to send this.
9747  theNym.IncrementRequestNum(
9748  theNym, strServerID); // since I used it for a
9749  // server request, I have
9750  // to increment it
9751 
9752  // (1) Set up member variables
9753  theMessage.m_strCommand = "notarizeTransactions";
9754  theMessage.m_strNymID = strNymID;
9755  theMessage.m_strServerID = strServerID;
9756  theMessage.SetAcknowledgments(
9757  theNym); // Must be called AFTER
9758  // theMessage.m_strServerID is already
9759  // set. (It uses it.)
9760 
9761  theMessage.m_strAcctID = str_MAIN_ACCOUNT_ID;
9762  theMessage.m_ascPayload = ascLedger;
9763 
9764  OTIdentifier NYMBOX_HASH;
9765  const std::string str_server(strServerID.Get());
9766  const bool bNymboxHash =
9767  theNym.GetNymboxHash(str_server, NYMBOX_HASH);
9768 
9769  if (bNymboxHash)
9770  NYMBOX_HASH.GetString(
9771  theMessage.m_strNymboxHash);
9772  else
9773  otErr << "Failed getting NymboxHash from Nym "
9774  "for server: " << str_server << "\n";
9775 
9776  // (2) Sign the Message
9777  theMessage.SignContract(theNym);
9778 
9779  // (3) Save the Message (with signatures and all,
9780  // back to its internal member m_strRawFile.)
9781  theMessage.SaveContract();
9782 
9783  bSendCommand = true;
9784  lReturnValue = lRequestNumber;
9785  } // Inbox loaded.
9786  } // successfully got first transaction number.
9787  else {
9788  otErr << "SUPPOSEDLY transaction numbers were "
9789  "available, but the call failed...\n";
9790  }
9791 
9792  } // theNym apparently has enough transaction numbers to
9793  // exchange the basket.
9794 
9795  } // loaded Basket Info from string.
9796  else {
9797  otOut << "Error loading basket info from asset contract. Are "
9798  "you SURE this is a basket currency?\n";
9799  }
9800  }
9801  else {
9802  otOut << "Failure loading or verifying " << strContractPath << "\n";
9803  }
9804  } break;
9805  case OTClient::issueBasket: // ISSUE BASKET
9806  {
9807  OTString strTemp, strBasketInfo;
9808 
9809  // Collect NUMBER OF CONTRACTS for the basket.
9810  otOut << "How many different asset types will compose this new basket? "
9811  "[2]: ";
9812  strTemp.OTfgets(std::cin);
9813  int32_t nBasketCount = atoi(strTemp.Get());
9814  if (0 >= nBasketCount) nBasketCount = 2;
9815 
9816  // Collect the MINIMUM TRANSFER AMOUNT for the basket. Default 100.
9817  otOut << "If your basket has a minimum transfer amount of 100, you "
9818  "might have 2 or 3 sub-currencies,\nwith the first being a "
9819  "minimum of 2 gold, the second being a minimum of 50 dollars, "
9820  "and the\nthird being a minimum of 30 silver. In this "
9821  "example, 100 units of the basket currency is\ntransferrable "
9822  "in or out of the basket currency, in return for 2 gold, 50 "
9823  "dollars, and 30 silver.\nAs those are only the *minimum* "
9824  "amounts, you could also transfer (in or out) in *any* "
9825  "multiple of\nthose numbers.\n\n";
9826  otOut << "What is the minimum transfer amount for the basket currency "
9827  "itself? [100]: ";
9828  strTemp.Release();
9829  strTemp.OTfgets(std::cin);
9830  int64_t lMinimumTransferAmount = atoi(strTemp.Get());
9831  if (0 == lMinimumTransferAmount) lMinimumTransferAmount = 100;
9832 
9833  // ADD THESE VALUES TO A BASKET OBJECT HERE SO I CAN RE-USE
9834  // lMinimumTransferAmount for the loop below.
9835  Basket theBasket(nBasketCount, lMinimumTransferAmount);
9836 
9837  // Collect all the contract IDs for the above contracts
9838  for (int32_t i = 0; i < nBasketCount; i++) {
9839  otOut << "Enter contract ID # " << (i + 1) << ": ";
9840  strTemp.Release();
9841  strTemp.OTfgets(std::cin);
9842 
9843  OTIdentifier SUB_CONTRACT_ID(strTemp.Get());
9844 
9845  // After each ID, collect the minimum transfer amount for EACH
9846  // contract.
9847  otOut << "Enter minimum transfer amount for that asset type: ";
9848  strTemp.Release();
9849  strTemp.OTfgets(std::cin);
9850 
9851  lMinimumTransferAmount = atol(strTemp.Get());
9852 
9853  // ADD THE CONTRACT ID TO A LIST TO BE SENT TO THE SERVER.
9854  // ADD THE MINIMUM TRANSFER AMOUNT TO THE SAME OBJECT
9855  theBasket.AddSubContract(SUB_CONTRACT_ID, lMinimumTransferAmount);
9856 
9857  // The object storing these should also have a space for storing
9858  // the account ID that will go with each one. The server will add
9859  // the Account ID when the reserve accounts are generated.
9860  //
9861  // (The basket issuer account will contain sub-accounts for the
9862  // reserves, which are stored there and 100% redeemable at all
9863  // times.)
9864  }
9865 
9866  // Export the Basket object into a string, add it as
9867  // a payload on message, and send to server.
9868  theBasket.SignContract(theNym);
9869  theBasket.SaveContractRaw(strBasketInfo);
9870 
9871  // The user signs and saves the contract, but once the server gets it,
9872  // the server releases signatures and signs it, calculating the hash
9873  // from the result,
9874  // in order to form the ID.
9875  //
9876  // The result is the same as any other currency contract, but with the
9877  // server's signature
9878  // on it (and thus it must store the server's public key). The server
9879  // handles all
9880  // transactions in and out of the basket currency based upon the rules
9881  // set up by the user.
9882  //
9883  // The user who created the currency has no more control over it. The
9884  // server reserves the
9885  // right to exchange out to the various users and close the basket.
9886 
9887  // AT SOME POINT, strBasketInfo has been populated with the relevant
9888  // data.
9889 
9890  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
9891  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
9892  theMessage.m_strRequestNum.Format(
9893  "%lld", lRequestNumber); // Always have to send this.
9894  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
9895  // a server request, I
9896  // have to increment it
9897 
9898  // (1) Set up member variables
9899  theMessage.m_strCommand = "issueBasket";
9900  theMessage.m_strNymID = strNymID;
9901  theMessage.m_strServerID = strServerID;
9902  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
9903  // theMessage.m_strServerID is
9904  // already set. (It uses it.)
9905 
9906  theMessage.m_ascPayload.SetString(strBasketInfo);
9907 
9908  // (2) Sign the Message
9909  theMessage.SignContract(theNym);
9910 
9911  // (3) Save the Message (with signatures and all, back to its internal
9912  // member m_strRawFile.)
9913  theMessage.SaveContract();
9914 
9915  bSendCommand = true;
9916  lReturnValue = lRequestNumber;
9917  } break;
9918  case OTClient::createAccount: // CREATE ACCOUNT
9919  {
9920  otOut << "Please enter an asset type (contract ID): ";
9921  // User input.
9922  // I need a from account
9923  OTString strAssetID;
9924  strAssetID.OTfgets(std::cin);
9925 
9926  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
9927  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
9928  theMessage.m_strRequestNum.Format(
9929  "%lld", lRequestNumber); // Always have to send this.
9930  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
9931  // a server request, I
9932  // have to increment it
9933 
9934  // (1) Set up member variables
9935  theMessage.m_strCommand = "createAccount";
9936  theMessage.m_strNymID = strNymID;
9937  theMessage.m_strServerID = strServerID;
9938  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
9939  // theMessage.m_strServerID is
9940  // already set. (It uses it.)
9941 
9942  theMessage.m_strAssetID =
9943  strAssetID; // the hash of the contract is the AssetID
9944 
9945  // (2) Sign the Message
9946  theMessage.SignContract(theNym);
9947 
9948  // (3) Save the Message (with signatures and all, back to its internal
9949  // member m_strRawFile.)
9950  theMessage.SaveContract();
9951 
9952  bSendCommand = true;
9953  lReturnValue = lRequestNumber;
9954  } break;
9955  case OTClient::notarizeTransfer: // NOTARIZE TRANSFER
9956  {
9957  OTString strFromAcct;
9958 
9959  if (nullptr == pAccount) {
9960  otOut << "Please enter an asset Account ID (FROM acct): ";
9961  // User input.
9962  // I need a from account
9963  strFromAcct.OTfgets(std::cin);
9964 
9965  if (strFromAcct.GetLength() < 2) return (-1);
9966 
9967  const OTIdentifier ACCOUNT_ID(strFromAcct);
9968 
9969  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
9970  pAccount->GetIdentifier(strFromAcct);
9971  CONTRACT_ID = pAccount->GetAssetTypeID();
9972  CONTRACT_ID.GetString(strContractID);
9973  }
9974  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
9975  strFromAcct.Get())) != nullptr) {
9976  pAccount->GetIdentifier(strFromAcct);
9977  CONTRACT_ID = pAccount->GetAssetTypeID();
9978  CONTRACT_ID.GetString(strContractID);
9979  }
9980  else {
9981  otErr << "Unable to transfer without a 'FROM' account. Try "
9982  "adding: --myacct ACCOUNT_ID\n";
9983  return (-1);
9984  }
9985  }
9986  else {
9987  pAccount->GetIdentifier(strFromAcct);
9988  CONTRACT_ID = pAccount->GetAssetTypeID();
9989  CONTRACT_ID.GetString(strContractID);
9990  }
9991 
9992  if (pAccount->GetPurportedServerID() != SERVER_ID) {
9993  otErr << "OTClient::ProcessUserCommand: "
9994  "pAccount->GetPurportedServerID() doesn't match "
9995  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
9996  return (-1);
9997  }
9998 
9999  OTString strRecipientAcct;
10000 
10001  if (nullptr == pHisAcctID) {
10002  otOut
10003  << "Enter Recipient's asset account ID (no partial IDs here): ";
10004 
10005  // User input.
10006  // I need a from account
10007  strRecipientAcct.OTfgets(std::cin);
10008 
10009  if (strRecipientAcct.GetLength() < 2) return (-1);
10010  }
10011  else {
10012  pHisAcctID->GetString(strRecipientAcct);
10013  }
10014 
10015  OTIdentifier HIS_ACCT_ID(strRecipientAcct);
10016 
10017  OTString strAmount;
10018  if (0 == lTransactionAmount) {
10019  otOut << "Please enter an amount: ";
10020  // User input.
10021  // I need an amount
10022  strAmount.OTfgets(std::cin);
10023  }
10024 
10025  const int64_t lTotalAmount =
10026  (0 == lTransactionAmount)
10027  ? // If nothing was passed in, then use atol(strAmount),
10028  (atol(strAmount.Exists() ? strAmount.Get() : "0"))
10029  : lTransactionAmount; // otherwise lTransactionAmount.
10030 
10031  OTIdentifier ACCT_FROM_ID(strFromAcct), USER_ID(theNym);
10032 
10033  int64_t lStoredTransactionNumber = 0;
10034  bool bGotTransNum = theNym.GetNextTransactionNum(
10035  theNym, strServerID, lStoredTransactionNumber); // this saves
10036 
10037  // int32_t GetTransactionNumCount(const OTIdentifier&
10038  // theServerID); // count
10039 
10040  if (bGotTransNum) {
10041  // Create a transaction
10043  USER_ID, ACCT_FROM_ID, SERVER_ID, OTTransaction::transfer,
10044  lStoredTransactionNumber);
10045 
10046  // set up the transaction item (each transaction may have multiple
10047  // items...)
10049  *pTransaction, OTItem::transfer, &HIS_ACCT_ID);
10050 
10051  pItem->SetAmount(lTotalAmount);
10052 
10053  OTString strNote(
10054  "Just testing the notes...blah blah blah blah blah blah");
10055  pItem->SetNote(strNote);
10056 
10057  // sign the item
10058  pItem->SignContract(theNym);
10059  pItem->SaveContract();
10060 
10061  pTransaction->AddItem(*pItem); // the Transaction's destructor will
10062  // cleanup the item. It "owns" it
10063  // now.
10064 
10065  std::unique_ptr<OTLedger> pInbox(pAccount->LoadInbox(theNym));
10066  std::unique_ptr<OTLedger> pOutbox(pAccount->LoadOutbox(theNym));
10067 
10068  if (nullptr == pInbox) {
10069  otOut << "Failed loading inbox!\n";
10070 
10071  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
10072  // NUMBERS.
10073  theNym.AddTransactionNum(theNym, strServerID,
10074  lStoredTransactionNumber,
10075  true); // bSave=true
10076  }
10077  else if (nullptr == pOutbox) {
10078  otOut << "Failed loading outbox!\n";
10079 
10080  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
10081  // NUMBERS.
10082  theNym.AddTransactionNum(theNym, strServerID,
10083  lStoredTransactionNumber,
10084  true); // bSave=true
10085  }
10086  // BALANCE AGREEMENT
10087  else {
10088  // Need to setup a dummy outbox transaction (to mimic the one
10089  // that will be on the server side when this pending transaction
10090  // is actually put into the real outbox.)
10091  // When the server adds its own, and then compares the two, they
10092  // should both show the same pending transaction, in order for
10093  // this balance agreement to be valid..
10094  // Otherwise the server would have to refuse it for being
10095  // inaccurate (server can't sign something inaccurate!) So I
10096  // throw a dummy on there before generating balance statement.
10097 
10098  OTTransaction* pOutboxTransaction = OTTransaction::GenerateTransaction(
10099  *pOutbox, OTTransaction::pending,
10100  1 /*todo pick some number that everyone agrees doesn't matter, like 1. The referring-to is the important
10101  number in this case, and perhaps server should update this value too before signing and returning.*/); // todo use a constant instead of '1'
10102 
10103  OT_ASSERT(nullptr != pOutboxTransaction); // for now.
10104 
10105  OTString strItem(*pItem);
10106  pOutboxTransaction->SetReferenceString(
10107  strItem); // So the GenerateBalanceStatement function below
10108  // can get the other info off this item (like
10109  // amount, etc)
10110  pOutboxTransaction->SetReferenceToNum(
10111  pItem->GetTransactionNum());
10112 
10113  // pOutboxTransaction->SignContract(theNym);
10114  // // Unnecessary to sign/save, since this is just a dummy data
10115  // for verification
10116  // pOutboxTransaction->SaveContract();
10117  // // purposes, and isn't being serialized anywhere.
10118 
10119  pOutbox->AddTransaction(
10120  *pOutboxTransaction); // no need to cleanup
10121  // pOutboxTransaction since pOutbox
10122  // will handle it now.
10123 
10124  // pBalanceItem is signed and saved within this call. No need to
10125  // do that again.
10126  OTItem* pBalanceItem = pInbox->GenerateBalanceStatement(
10127  atol(strAmount.Get()) * (-1), *pTransaction, theNym,
10128  *pAccount, *pOutbox);
10129 
10130  if (nullptr !=
10131  pBalanceItem) // will never be nullptr. Will assert
10132  // above before it gets here.
10133  pTransaction->AddItem(*pBalanceItem); // Better not be
10134  // nullptr... message
10135  // will fail... But
10136  // better check
10137  // anyway.
10138 
10139  // sign the transaction
10140  pTransaction->SignContract(theNym);
10141  pTransaction->SaveContract();
10142 
10143  // set up the ledger
10144  OTLedger theLedger(USER_ID, ACCT_FROM_ID, SERVER_ID);
10145  theLedger.GenerateLedger(ACCT_FROM_ID, SERVER_ID,
10146  OTLedger::message); // bGenerateLedger
10147  // defaults to
10148  // false, which is
10149  // correct.
10150  theLedger.AddTransaction(*pTransaction);
10151 
10152  // sign the ledger
10153  theLedger.SignContract(theNym);
10154  theLedger.SaveContract();
10155 
10156  // extract the ledger in ascii-armored form
10157  OTString strLedger(theLedger);
10158  OTASCIIArmor ascLedger; // I can't pass strLedger into this
10159  // constructor because I want to encode
10160  // it
10161 
10162  // Encoding...
10163  ascLedger.SetString(strLedger);
10164 
10165  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10166  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
10167  theMessage.m_strRequestNum.Format(
10168  "%lld", lRequestNumber); // Always have to send this.
10169  theNym.IncrementRequestNum(
10170  theNym, strServerID); // since I used it for a server
10171  // request, I have to increment it
10172 
10173  // (1) Set up member variables
10174  theMessage.m_strCommand = "notarizeTransactions";
10175  theMessage.m_strNymID = strNymID;
10176  theMessage.m_strServerID = strServerID;
10177  theMessage.SetAcknowledgments(
10178  theNym); // Must be called AFTER theMessage.m_strServerID is
10179  // already set. (It uses it.)
10180 
10181  theMessage.m_strAcctID = strFromAcct;
10182  theMessage.m_ascPayload = ascLedger;
10183 
10184  OTIdentifier NYMBOX_HASH;
10185  const std::string str_server(strServerID.Get());
10186  const bool bNymboxHash =
10187  theNym.GetNymboxHash(str_server, NYMBOX_HASH);
10188 
10189  if (bNymboxHash)
10190  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
10191  else
10192  otErr << "Failed getting NymboxHash from Nym for server: "
10193  << str_server << "\n";
10194 
10195  // (2) Sign the Message
10196  theMessage.SignContract(theNym);
10197 
10198  // (3) Save the Message (with signatures and all, back to its
10199  // internal member m_strRawFile.)
10200  theMessage.SaveContract();
10201 
10202  bSendCommand = true;
10203  lReturnValue = lRequestNumber;
10204  }
10205 
10206  }
10207  else {
10208  otOut << "No transaction numbers were available. Suggest "
10209  "requesting the server for one.\n";
10210  }
10211  } break;
10212  case OTClient::setAssetName: // SET ASSET CONTRACT NAME (wallet label only)
10213  {
10214  OT_ASSERT(nullptr != m_pWallet);
10215 
10216  otOut << "Please enter an Asset Type ID: ";
10217  // User input.
10218  // I need a server ID
10219  OTString strContractID;
10220  strContractID.OTfgets(std::cin);
10221 
10222  const OTIdentifier theTargetID(strContractID);
10223 
10224  OTAssetContract* pTargetContract =
10225  m_pWallet->GetAssetContract(theTargetID);
10226 
10227  if (nullptr != pTargetContract) {
10228  otOut << "Enter the new client-side \"name\" label for that asset "
10229  "type: ";
10230  // User input.
10231  // I need a name
10232  OTString strNewName;
10233  strNewName.OTfgets(std::cin);
10234 
10235  pTargetContract->SetName(strNewName);
10236 
10237  m_pWallet->SaveWallet(); // Only 'cause the server's name is stored
10238  // here.
10239  }
10240  else {
10241  otOut << "No Asset Contract found with that ID. Try 'load'.\n";
10242  }
10243  } break;
10244  case OTClient::setServerName: // SET SERVER CONTRACT NAME (wallet label
10245  // only)
10246  {
10247  OT_ASSERT(nullptr != m_pWallet);
10248 
10249  otOut << "Please enter a Server ID: ";
10250  // User input.
10251  // I need a server ID
10252  OTString strContractID;
10253  strContractID.OTfgets(std::cin);
10254 
10255  const OTIdentifier theTargetID(strContractID);
10256 
10257  OTServerContract* pTargetContract =
10258  m_pWallet->GetServerContract(theTargetID);
10259 
10260  if (nullptr != pTargetContract) {
10261  otOut << "Enter the new client-side \"name\" label for that "
10262  "transaction server: ";
10263  // User input.
10264  // I need a name
10265  OTString strNewName;
10266  strNewName.OTfgets(std::cin);
10267 
10268  pTargetContract->SetName(strNewName);
10269 
10270  m_pWallet->SaveWallet(); // Only 'cause the server's name is
10271  // stored
10272  // here.
10273  }
10274  else {
10275  otOut << "No Server Contract found with that ID. Try 'load'.\n";
10276  }
10277  }
10278  break;
10279  case OTClient::setNymName: // SET NYM NAME (wallet label only)
10280  {
10281  OT_ASSERT(nullptr != m_pWallet);
10282 
10283  otOut << "Please enter a Nym ID: ";
10284  // User input.
10285  // I need a nym ID
10286  OTString strNymID;
10287  strNymID.OTfgets(std::cin);
10288 
10289  const OTIdentifier theTargetNymID(strNymID);
10290 
10291  OTPseudonym* pTargetNym = m_pWallet->GetNymByID(theTargetNymID);
10292 
10293  if (nullptr != pTargetNym) {
10294  otOut << "Enter the new client-side \"name\" label for that Nym: ";
10295  // User input.
10296  // I need a name
10297  OTString strNewName;
10298  strNewName.OTfgets(std::cin);
10299 
10300  OTString strOldName(pTargetNym->GetNymName()); // just in case.
10301 
10302  pTargetNym->SetNymName(strNewName);
10303 
10304  if (pTargetNym->SaveSignedNymfile(theNym)) // theNym is signer on
10305  // this file.
10306  {
10307  m_pWallet->SaveWallet(); // Only 'cause the nym's name is stored
10308  // here, too.
10309  }
10310  else
10311  pTargetNym->SetNymName(strOldName);
10312  }
10313  else {
10314  otOut << "No Nym found with that ID. Try 'load'.\n";
10315  }
10316  } break;
10317  case OTClient::setAccountName: // SET ACCOUNT NAME (wallet label only)
10318  {
10319  OT_ASSERT(nullptr != m_pWallet);
10320 
10321  otOut << "Please enter an asset account ID: ";
10322  // User input.
10323  // I need an account
10324  OTString strAcctID;
10325  strAcctID.OTfgets(std::cin);
10326 
10327  if (strAcctID.GetLength() < 2) return (-1);
10328 
10329  const OTIdentifier theAccountID(strAcctID);
10330 
10331  OTAccount* pTheAccount = m_pWallet->GetAccount(theAccountID);
10332 
10333  if (nullptr != pTheAccount) {
10334  otOut << "Enter the new client-side \"name\" label for that "
10335  "Account: ";
10336  // User input.
10337  // I need a name
10338  OTString strNewName;
10339  strNewName.OTfgets(std::cin);
10340 
10341  pTheAccount->SetName(strNewName);
10342  pTheAccount->ReleaseSignatures();
10343 
10344  pTheAccount->SignContract(theNym);
10345  pTheAccount->SaveAccount();
10346 
10347  m_pWallet->SaveWallet();
10348  }
10349  else {
10350  otOut << "No account found with that ID. Try 'load'.\n";
10351  }
10352  } break;
10353  case OTClient::getNymbox: // GET NYMBOX
10354  {
10355 
10356  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10357  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
10358  theMessage.m_strRequestNum.Format(
10359  "%lld", lRequestNumber); // Always have to send this.
10360  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
10361  // a server request, I
10362  // have to increment it
10363 
10364  // (1) Set up member variables
10365  theMessage.m_strCommand = "getNymbox";
10366  theMessage.m_strNymID = strNymID;
10367  theMessage.m_strServerID = strServerID;
10368  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
10369  // theMessage.m_strServerID is
10370  // already set. (It uses it.)
10371 
10372  // (2) Sign the Message
10373  theMessage.SignContract(theNym);
10374 
10375  // (3) Save the Message (with signatures and all, back to its internal
10376  // member m_strRawFile.)
10377  theMessage.SaveContract();
10378 
10379  bSendCommand = true;
10380  lReturnValue = lRequestNumber;
10381  } break;
10382  case OTClient::getInbox: // GET INBOX
10383  {
10384 
10385  OTString strFromAcct;
10386 
10387  if (nullptr == pAccount) {
10388  otOut << "Please enter an asset Account ID (to get its INBOX): ";
10389  // User input.
10390  // I need a from account
10391  strFromAcct.OTfgets(std::cin);
10392 
10393  if (strFromAcct.GetLength() < 2) return (-1);
10394 
10395  const OTIdentifier ACCOUNT_ID(strFromAcct);
10396 
10397  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
10398  pAccount->GetIdentifier(strFromAcct);
10399  CONTRACT_ID = pAccount->GetAssetTypeID();
10400  CONTRACT_ID.GetString(strContractID);
10401  }
10402  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
10403  strFromAcct.Get())) != nullptr) {
10404  pAccount->GetIdentifier(strFromAcct);
10405  CONTRACT_ID = pAccount->GetAssetTypeID();
10406  CONTRACT_ID.GetString(strContractID);
10407  }
10408  else {
10409  otErr << "Unable to download Inbox without an account ID. Try "
10410  "adding: --myacct ACCOUNT_ID\n";
10411  return (-1);
10412  }
10413  }
10414  else {
10415  pAccount->GetIdentifier(strFromAcct);
10416  CONTRACT_ID = pAccount->GetAssetTypeID();
10417  CONTRACT_ID.GetString(strContractID);
10418  }
10419 
10420  if (pAccount->GetPurportedServerID() != SERVER_ID) {
10421  otErr << "OTClient::ProcessUserCommand: "
10422  "pAccount->GetPurportedServerID() doesn't match "
10423  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
10424  return (-1);
10425  }
10426 
10427  OTString strAcctID;
10428  OTIdentifier theAccountID;
10429 
10430  pAccount->GetIdentifier(theAccountID);
10431  theAccountID.GetString(strAcctID);
10432 
10433  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10434  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
10435  theMessage.m_strRequestNum.Format(
10436  "%lld", lRequestNumber); // Always have to send this.
10437  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
10438  // a server request, I
10439  // have to increment it
10440 
10441  // (1) Set up member variables
10442  theMessage.m_strCommand = "getInbox";
10443  theMessage.m_strNymID = strNymID;
10444  theMessage.m_strServerID = strServerID;
10445  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
10446  // theMessage.m_strServerID is
10447  // already set. (It uses it.)
10448 
10449  theMessage.m_strAcctID = strAcctID;
10450 
10451  // (2) Sign the Message
10452  theMessage.SignContract(theNym);
10453 
10454  // (3) Save the Message (with signatures and all, back to its internal
10455  // member m_strRawFile.)
10456  theMessage.SaveContract();
10457 
10458  bSendCommand = true;
10459  lReturnValue = lRequestNumber;
10460  } break;
10461  case OTClient::getOutbox: // GET OUTBOX
10462  {
10463 
10464  OTString strFromAcct;
10465 
10466  if (nullptr == pAccount) {
10467  otOut << "Please enter an asset Account ID (to get its OUTBOX): ";
10468  // User input.
10469  // I need a from account
10470  strFromAcct.OTfgets(std::cin);
10471 
10472  if (strFromAcct.GetLength() < 2) return (-1);
10473 
10474  const OTIdentifier ACCOUNT_ID(strFromAcct);
10475 
10476  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
10477  pAccount->GetIdentifier(strFromAcct);
10478  CONTRACT_ID = pAccount->GetAssetTypeID();
10479  CONTRACT_ID.GetString(strContractID);
10480  }
10481  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
10482  strFromAcct.Get())) != nullptr) {
10483  pAccount->GetIdentifier(strFromAcct);
10484  CONTRACT_ID = pAccount->GetAssetTypeID();
10485  CONTRACT_ID.GetString(strContractID);
10486  }
10487  else {
10488  otErr << "Unable to download outbox without account ID. Try "
10489  "adding: --myacct ACCOUNT_ID\n";
10490  return (-1);
10491  }
10492  }
10493  else {
10494  pAccount->GetIdentifier(strFromAcct);
10495  CONTRACT_ID = pAccount->GetAssetTypeID();
10496  CONTRACT_ID.GetString(strContractID);
10497  }
10498 
10499  if (pAccount->GetPurportedServerID() != SERVER_ID) {
10500  otErr << "OTClient::ProcessUserCommand: "
10501  "pAccount->GetPurportedServerID() doesn't match "
10502  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
10503  return (-1);
10504  }
10505 
10506  OTString strAcctID;
10507  OTIdentifier theAccountID;
10508 
10509  pAccount->GetIdentifier(theAccountID);
10510  theAccountID.GetString(strAcctID);
10511 
10512  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10513  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
10514  theMessage.m_strRequestNum.Format(
10515  "%lld", lRequestNumber); // Always have to send this.
10516  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
10517  // a server request, I
10518  // have to increment it
10519 
10520  // (1) Set up member variables
10521  theMessage.m_strCommand = "getOutbox";
10522  theMessage.m_strNymID = strNymID;
10523  theMessage.m_strServerID = strServerID;
10524  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
10525  // theMessage.m_strServerID is
10526  // already set. (It uses it.)
10527 
10528  theMessage.m_strAcctID = strAcctID;
10529 
10530  // (2) Sign the Message
10531  theMessage.SignContract(theNym);
10532 
10533  // (3) Save the Message (with signatures and all, back to its internal
10534  // member m_strRawFile.)
10535  theMessage.SaveContract();
10536 
10537  bSendCommand = true;
10538  lReturnValue = lRequestNumber;
10539  }
10540 
10541  /*
10542  bool OTClient::ProcessUserCommand(OTClient::OT_CLIENT_CMD_TYPE
10543  requestedCommand, OTMessage& theMessage, OTPseudonym& theNym,
10544  // OTAssetContract& theContract,
10545  OTServerContract& theServer,
10546  OTAccount * pAccount=nullptr,
10547  int64_t lTransactionAmount=0,
10548  OTAssetContract * pMyAssetContract=nullptr,
10549  OTIdentifier * pHisNymID=nullptr,
10550  OTIdentifier * pHisAcctID=nullptr)
10551  {
10552  // This is all preparatory work to get the various pieces of data together
10553  -- only
10554  // then can we put those pieces into a message.
10555  OTIdentifier CONTRACT_ID;
10556  OTString strNymID, strContractID, strServerID, strNymPublicKey,
10557  strAccountID;
10558  int64_t lRequestNumber = 0;
10559 
10560  theNym.GetIdentifier(strNymID);
10561  theServer.GetIdentifier(strServerID);
10562 
10563  const OTIdentifier SERVER_ID(strServerID);
10564  */
10565 
10566  // This is called by the command line user.
10567  break;
10568  case OTClient::processEntireNymbox: // PROCESS ENTIRE NYMBOX
10569  {
10570  const OTIdentifier MY_NYM_ID(theNym);
10571 
10572  // Load up the appropriate Nymbox...
10573  OTLedger theNymbox(MY_NYM_ID, MY_NYM_ID, SERVER_ID);
10574 
10575  bool bSuccess = false;
10576  bool bLoadedNymbox = theNymbox.LoadNymbox();
10577  bool bVerifiedNymbox =
10578  bLoadedNymbox ? theNymbox.VerifyAccount(theNym) : false;
10579  bool bIsEmpty = theNymbox.GetTransactionCount() < 1;
10580 
10581  if (!bLoadedNymbox)
10582  otOut << "OTClient::ProcessUserCommand::processEntireNymbox: "
10583  "Failed loading Nymbox: " << strNymID << " \n";
10584  else if (!bVerifiedNymbox)
10585  otOut << "OTClient::ProcessUserCommand::processEntireNymbox: "
10586  "Failed verifying Nymbox: " << strNymID << " \n";
10587  else if (!bIsEmpty)
10588  bSuccess = AcceptEntireNymbox(theNymbox, SERVER_ID, theServer,
10589  theNym, theMessage);
10590 
10591  if (!bSuccess) {
10592  if (bIsEmpty)
10593  otOut << "OTClient::ProcessUserCommand::processEntireNymbox: "
10594  "Nymbox (" << strNymID
10595  << ") is empty (so, skipping processNymbox.)\n";
10596  else
10597  otOut << "OTClient::ProcessUserCommand::processEntireNymbox: "
10598  "Failed trying to accept the entire Nymbox.\n";
10599  }
10600  else {
10601  // (2) Sign the Message
10602  theMessage.SignContract(theNym);
10603 
10604  // (3) Save the Message (with signatures and all, back to its
10605  // internal member m_strRawFile.)
10606  theMessage.SaveContract();
10607 
10608  bSendCommand = true;
10609  }
10610  }
10611 
10612  // This is called by AcceptEntireNymbox().
10613  break;
10614  case OTClient::processNymbox: // PROCESS NYMBOX
10615  {
10616  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10617  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
10618  theMessage.m_strRequestNum.Format(
10619  "%lld", lRequestNumber); // Always have to send this.
10620  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
10621  // a server request, I
10622  // have to increment it
10623 
10624  // (1) Set up member variables
10625  theMessage.m_strCommand = "processNymbox";
10626  theMessage.m_strNymID = strNymID;
10627  theMessage.m_strServerID = strServerID;
10628  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
10629  // theMessage.m_strServerID is
10630  // already set. (It uses it.)
10631 
10632  OTIdentifier EXISTING_NYMBOX_HASH;
10633  const std::string str_server_id(strServerID.Get());
10634 
10635  const bool bSuccess =
10636  theNym.GetNymboxHash(str_server_id, EXISTING_NYMBOX_HASH);
10637 
10638  if (bSuccess)
10639  EXISTING_NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
10640 
10641  // (2) Sign the Message
10642  theMessage.SignContract(theNym);
10643 
10644  // (3) Save the Message (with signatures and all, back to its internal
10645  // member m_strRawFile.)
10646  theMessage.SaveContract();
10647 
10648  bSendCommand = true;
10649  lReturnValue = lRequestNumber;
10650  }
10651 
10652  // This is called by the user of the command line utility.
10653  //
10654  break;
10655  case OTClient::processEntireInbox: // PROCESS ENTIRE INBOX
10656  {
10657  const OTIdentifier MY_NYM_ID(theNym);
10658 
10659  OTString strFromAcct;
10660 
10661  if (nullptr == pAccount) {
10662  otOut
10663  << "Please enter an asset Account ID (to PROCESS its INBOX): ";
10664  // User input.
10665  // I need a from account
10666  strFromAcct.OTfgets(std::cin);
10667 
10668  if (strFromAcct.GetLength() < 2) return (-1);
10669 
10670  const OTIdentifier ACCOUNT_ID(strFromAcct);
10671 
10672  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
10673  pAccount->GetIdentifier(strFromAcct);
10674  CONTRACT_ID = pAccount->GetAssetTypeID();
10675  CONTRACT_ID.GetString(strContractID);
10676  }
10677  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
10678  strFromAcct.Get())) != nullptr) {
10679  pAccount->GetIdentifier(strFromAcct);
10680  CONTRACT_ID = pAccount->GetAssetTypeID();
10681  CONTRACT_ID.GetString(strContractID);
10682  }
10683  else {
10684  otErr << "Unable to process inbox without account ID. Try "
10685  "adding: --myacct ACCOUNT_ID\n";
10686  return (-1);
10687  }
10688  }
10689  else {
10690  pAccount->GetIdentifier(strFromAcct);
10691  CONTRACT_ID = pAccount->GetAssetTypeID();
10692  CONTRACT_ID.GetString(strContractID);
10693  }
10694 
10695  if (pAccount->GetPurportedServerID() != SERVER_ID) {
10696  otErr << "OTClient::ProcessUserCommand: "
10697  "pAccount->GetPurportedServerID() doesn't match "
10698  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
10699  return (-1);
10700  }
10701 
10702  OTIdentifier theAccountID;
10703  pAccount->GetIdentifier(theAccountID);
10704 
10705  // Load up the appropriate Inbox...
10706  OTLedger theInbox(MY_NYM_ID, theAccountID, SERVER_ID);
10707 
10708  bool bLoadedInbox = theInbox.LoadInbox();
10709 
10710  bool bVerifiedInbox =
10711  (bLoadedInbox ? theInbox.VerifyAccount(theNym) : false);
10712 
10713  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10714  // (1) Set up member variables
10715  bool bSuccess = (bLoadedInbox && bVerifiedInbox &&
10716  AcceptEntireInbox(theInbox, SERVER_ID, theServer,
10717  theNym, theMessage, *pAccount));
10718  // -----------------
10719 
10720  if (bSuccess) {
10721  // (2) Sign the Message
10722  theMessage.SignContract(theNym);
10723 
10724  // (3) Save the Message (with signatures and all, back to its
10725  // internal member m_strRawFile.)
10726  theMessage.SaveContract();
10727 
10728  bSendCommand = true;
10729  }
10730  else {
10731  otOut << "OTClient::processEntireInbox: Failure Inbox: Loading ("
10732  << (bLoadedInbox ? "Success" : "Failure") << "), verifying ("
10733  << (bVerifiedInbox ? "Success" : "Failure")
10734  << "), or accepting (" << (bSuccess ? "Success" : "Failure")
10735  << ") entire Inbox.\n";
10736  }
10737  }
10738 
10739  // This is called by AcceptEntireInbox().
10740  //
10741  break;
10742  case OTClient::processInbox: // PROCESS INBOX
10743  {
10744  OTString strFromAcct;
10745 
10746  if (nullptr == pAccount) {
10747  otOut
10748  << "Please enter an asset Account ID (to PROCESS its INBOX): ";
10749  // User input.
10750  // I need a from account
10751  strFromAcct.OTfgets(std::cin);
10752 
10753  if (strFromAcct.GetLength() < 2) return (-1);
10754 
10755  const OTIdentifier ACCOUNT_ID(strFromAcct);
10756 
10757  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
10758  pAccount->GetIdentifier(strFromAcct);
10759  CONTRACT_ID = pAccount->GetAssetTypeID();
10760  CONTRACT_ID.GetString(strContractID);
10761  }
10762  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
10763  strFromAcct.Get())) != nullptr) {
10764  pAccount->GetIdentifier(strFromAcct);
10765  CONTRACT_ID = pAccount->GetAssetTypeID();
10766  CONTRACT_ID.GetString(strContractID);
10767  }
10768  else {
10769  otErr << "Unable to process inbox without account ID. Try "
10770  "adding: --myacct ACCOUNT_ID\n";
10771  return (-1);
10772  }
10773  }
10774  else {
10775  pAccount->GetIdentifier(strFromAcct);
10776  CONTRACT_ID = pAccount->GetAssetTypeID();
10777  CONTRACT_ID.GetString(strContractID);
10778  }
10779 
10780  if (pAccount->GetPurportedServerID() != SERVER_ID) {
10781  otErr << "OTClient::ProcessUserCommand: "
10782  "pAccount->GetPurportedServerID() doesn't match "
10783  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
10784  return (-1);
10785  }
10786 
10787  OTString strAcctID;
10788  OTIdentifier theAccountID;
10789 
10790  pAccount->GetIdentifier(theAccountID);
10791  theAccountID.GetString(strAcctID);
10792 
10793  // Normally processInbox command is sent with a transaction ledger
10794  // in the payload, accepting or rejecting various transactions in
10795  // my inbox.
10796  // If pAccount was passed in, that means somewhere else in the code
10797  // a ledger is being added to this message after this point, and it
10798  // is being re-signed and sent out.
10799  // That's why you don't see a ledger being constructed and added to
10800  // the payload here. Because it's being done somewhere else, and that
10801  // same place is what passed the account pointer in here.
10802  // I only put this block here for now because I'd rather have it with
10803  // all the others.
10804 
10805  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10806  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
10807  theMessage.m_strRequestNum.Format(
10808  "%lld", lRequestNumber); // Always have to send this.
10809  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
10810  // a server request, I
10811  // have to increment it
10812 
10813  // (1) Set up member variables
10814  theMessage.m_strCommand = "processInbox";
10815  theMessage.m_strNymID = strNymID;
10816  theMessage.m_strServerID = strServerID;
10817  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
10818  // theMessage.m_strServerID is
10819  // already set. (It uses it.)
10820 
10821  theMessage.m_strAcctID = strAcctID;
10822 
10823  OTIdentifier NYMBOX_HASH;
10824  const std::string str_server(strServerID.Get());
10825  const bool bNymboxHash = theNym.GetNymboxHash(str_server, NYMBOX_HASH);
10826 
10827  if (bNymboxHash)
10828  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
10829  else
10830  otErr << "Failed getting NymboxHash from Nym for server: "
10831  << str_server << "\n";
10832 
10833  // (2) Sign the Message
10834  theMessage.SignContract(theNym);
10835 
10836  // (3) Save the Message (with signatures and all, back to its internal
10837  // member m_strRawFile.)
10838  theMessage.SaveContract();
10839 
10840  bSendCommand = true;
10841  lReturnValue = lRequestNumber;
10842  } break;
10843  case OTClient::getAccount: // GET ACCOUNT
10844  {
10845  OTString strFromAcct;
10846 
10847  if (nullptr == pAccount) {
10848  otOut << "Please enter an asset Account ID (to download its "
10849  "intermediary file): ";
10850  // User input.
10851  // I need a from account
10852  strFromAcct.OTfgets(std::cin);
10853 
10854  if (strFromAcct.GetLength() < 2) return (-1);
10855 
10856  const OTIdentifier ACCOUNT_ID(strFromAcct);
10857 
10858  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
10859  pAccount->GetIdentifier(strFromAcct);
10860  CONTRACT_ID = pAccount->GetAssetTypeID();
10861  CONTRACT_ID.GetString(strContractID);
10862  }
10863  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
10864  strFromAcct.Get())) != nullptr) {
10865  pAccount->GetIdentifier(strFromAcct);
10866  CONTRACT_ID = pAccount->GetAssetTypeID();
10867  CONTRACT_ID.GetString(strContractID);
10868  }
10869  else {
10870  otErr << "Unable to download account without account ID. Try "
10871  "adding: --myacct ACCOUNT_ID\n";
10872  return (-1);
10873  }
10874  }
10875  else {
10876  pAccount->GetIdentifier(strFromAcct);
10877  CONTRACT_ID = pAccount->GetAssetTypeID();
10878  CONTRACT_ID.GetString(strContractID);
10879  }
10880 
10881  if (pAccount->GetPurportedServerID() != SERVER_ID) {
10882  otErr << "OTClient::ProcessUserCommand: "
10883  "pAccount->GetPurportedServerID() doesn't match "
10884  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
10885  return (-1);
10886  }
10887 
10888  OTString strAcctID;
10889  OTIdentifier theAccountID;
10890 
10891  pAccount->GetIdentifier(theAccountID);
10892  theAccountID.GetString(strAcctID);
10893 
10894  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10895  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
10896  theMessage.m_strRequestNum.Format(
10897  "%lld", lRequestNumber); // Always have to send this.
10898  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
10899  // a server request, I
10900  // have to increment it
10901 
10902  // (1) Set up member variables
10903  theMessage.m_strCommand = "getAccount";
10904  theMessage.m_strNymID = strNymID;
10905  theMessage.m_strServerID = strServerID;
10906  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
10907  // theMessage.m_strServerID is
10908  // already set. (It uses it.)
10909 
10910  theMessage.m_strAcctID = strAcctID;
10911 
10912  // (2) Sign the Message
10913  theMessage.SignContract(theNym);
10914 
10915  // (3) Save the Message (with signatures and all, back to its internal
10916  // member m_strRawFile.)
10917  theMessage.SaveContract();
10918 
10919  bSendCommand = true;
10920  lReturnValue = lRequestNumber;
10921  } break;
10922  case OTClient::getContract: // GET CONTRACT
10923  {
10924  otOut << "Please enter an asset type ID: ";
10925  // User input.
10926  // I need an account
10927  OTString strAssetID;
10928  strAssetID.OTfgets(std::cin);
10929 
10930  if (strAssetID.GetLength() < 2) return (-1);
10931 
10932  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10933  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
10934  theMessage.m_strRequestNum.Format(
10935  "%lld", lRequestNumber); // Always have to send this.
10936  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
10937  // a server request, I
10938  // have to increment it
10939 
10940  // (1) Set up member variables
10941  theMessage.m_strCommand = "getContract";
10942  theMessage.m_strNymID = strNymID;
10943  theMessage.m_strServerID = strServerID;
10944  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
10945  // theMessage.m_strServerID is
10946  // already set. (It uses it.)
10947 
10948  theMessage.m_strAssetID = strAssetID;
10949 
10950  // (2) Sign the Message
10951  theMessage.SignContract(theNym);
10952 
10953  // (3) Save the Message (with signatures and all, back to its internal
10954  // member m_strRawFile.)
10955  theMessage.SaveContract();
10956 
10957  bSendCommand = true;
10958  lReturnValue = lRequestNumber;
10959  } break;
10960  case OTClient::getMint: // GET MINT
10961  {
10962  otOut << "Please enter an asset type ID: ";
10963  // User input.
10964  // I need an account
10965  OTString strAssetID;
10966  strAssetID.OTfgets(std::cin);
10967 
10968  if (strAssetID.GetLength() < 2) return (-1);
10969 
10970  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
10971  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
10972  theMessage.m_strRequestNum.Format(
10973  "%lld", lRequestNumber); // Always have to send this.
10974  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
10975  // a server request, I
10976  // have to increment it
10977 
10978  // (1) Set up member variables
10979  theMessage.m_strCommand = "getMint";
10980  theMessage.m_strNymID = strNymID;
10981  theMessage.m_strServerID = strServerID;
10982  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
10983  // theMessage.m_strServerID is
10984  // already set. (It uses it.)
10985 
10986  theMessage.m_strAssetID = strAssetID;
10987 
10988  // (2) Sign the Message
10989  theMessage.SignContract(theNym);
10990 
10991  // (3) Save the Message (with signatures and all, back to its internal
10992  // member m_strRawFile.)
10993  theMessage.SaveContract();
10994 
10995  bSendCommand = true;
10996  lReturnValue = lRequestNumber;
10997  } break;
10998  case OTClient::notarizeDeposit: // NOTARIZE DEPOSIT
10999  {
11000  OTString strFromAcct;
11001 
11002  if (nullptr == pAccount) {
11003  otOut << "Please enter an asset Account ID to deposit your tokens "
11004  "to: ";
11005  // User input.
11006  // I need a from account
11007  strFromAcct.OTfgets(std::cin);
11008 
11009  if (strFromAcct.GetLength() < 2) return (-1);
11010 
11011  const OTIdentifier ACCOUNT_ID(strFromAcct);
11012 
11013  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
11014  pAccount->GetIdentifier(strFromAcct);
11015  CONTRACT_ID = pAccount->GetAssetTypeID();
11016  CONTRACT_ID.GetString(strContractID);
11017  }
11018  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
11019  strFromAcct.Get())) != nullptr) {
11020  pAccount->GetIdentifier(strFromAcct);
11021  CONTRACT_ID = pAccount->GetAssetTypeID();
11022  CONTRACT_ID.GetString(strContractID);
11023  }
11024  else {
11025  otErr << "Unable to deposit tokens without an account. Try "
11026  "adding: --myacct ACCOUNT_ID\n";
11027  return (-1);
11028  }
11029  }
11030  else {
11031  pAccount->GetIdentifier(strFromAcct);
11032  CONTRACT_ID = pAccount->GetAssetTypeID();
11033  CONTRACT_ID.GetString(strContractID);
11034  }
11035 
11036  if (pAccount->GetPurportedServerID() != SERVER_ID) {
11037  otErr << "OTClient::ProcessUserCommand: "
11038  "pAccount->GetPurportedServerID() doesn't match "
11039  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
11040  return (-1);
11041  }
11042 
11043  const OTIdentifier ACCT_FROM_ID(strFromAcct), USER_ID(theNym);
11044 
11045  Purse thePurse(SERVER_ID, CONTRACT_ID);
11046 
11047  const OTPseudonym* pServerNym = theServer.GetContractPublicNym();
11048 
11049  int64_t lStoredTransactionNumber = 0;
11050  bool bGotTransNum = false;
11051 
11052  std::unique_ptr<OTLedger> pInbox(pAccount->LoadInbox(theNym));
11053  std::unique_ptr<OTLedger> pOutbox(pAccount->LoadOutbox(theNym));
11054 
11055  if (nullptr == pInbox) {
11056  otOut << "Failed loading inbox!\n";
11057  break;
11058  }
11059 
11060  if (nullptr == pOutbox) {
11061  otOut << "Failed loading outbox!\n";
11062  break;
11063  }
11064 
11065  bGotTransNum = theNym.GetNextTransactionNum(theNym, strServerID,
11066  lStoredTransactionNumber);
11067  if (!bGotTransNum) {
11068  otOut << "No Transaction Numbers were available. Try requesting "
11069  "the server for a new one.\n";
11070  break;
11071  }
11072 
11073  if (!pServerNym) {
11074  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
11075  // NUMBERS.
11076  theNym.AddTransactionNum(theNym, strServerID,
11077  lStoredTransactionNumber,
11078  true); // bSave=true
11079  break;
11080  }
11081 
11082  bool bSuccess = false;
11083 
11084  // Create a transaction
11086  USER_ID, ACCT_FROM_ID, SERVER_ID, OTTransaction::deposit,
11087  lStoredTransactionNumber);
11088 
11089  // set up the transaction item (each transaction may have multiple
11090  // items...)
11091  OTItem* pItem =
11093 
11094  OTString strNote("Deposit this cash, please!");
11095  pItem->SetNote(strNote);
11096 
11097  otOut << "How many tokens would you like to deposit? ";
11098  OTString strTokenCount;
11099  strTokenCount.OTfgets(std::cin);
11100  const int32_t nTokenCount = atoi(strTokenCount.Get());
11101 
11102  OTNym_or_SymmetricKey theNymAsOwner(theNym),
11103  theServerNymAsOwner(*pServerNym);
11104 
11105  for (int32_t nTokenIndex = 1; nTokenIndex <= nTokenCount;
11106  nTokenIndex++) {
11107  otOut << "Please enter plaintext token # " << nTokenIndex
11108  << "; terminate with ~ on a new line:\n> ";
11109  OTString strToken;
11110  char decode_buffer[200]; // Safe since we only read
11111  // sizeof(decode_buffer)-1
11112 
11113  do {
11114  decode_buffer[0] = 0; // Make it fresh.
11115 
11116  if (!fgets(decode_buffer, sizeof(decode_buffer) - 1, stdin) &&
11117  (decode_buffer[0] != '~')) {
11118  strToken.Concatenate("%s", decode_buffer);
11119  otOut << "> ";
11120  }
11121  else {
11122  break;
11123  }
11124 
11125  } while (decode_buffer[0] != '~');
11126 
11127  // Create the relevant token request with same server/asset ID as
11128  // the purse.
11129  // the purse does NOT own the token at this point. the token's
11130  // constructor
11131  // just uses it to copy some IDs, since they must match.
11132  std::unique_ptr<Token> pToken(
11133  Token::TokenFactory(strToken, thePurse));
11134  OT_ASSERT(nullptr != pToken);
11135 
11136  if (nullptr != pToken) // TODO verify the token contract
11137  {
11138  // TODO need 2-recipient envelopes. My request to the server is
11139  // encrypted to the server's nym,
11140  // but it should be encrypted to my Nym also, so both have
11141  // access to decrypt it.
11142 
11143  // Now the token is ready, let's add it to a purse
11144  // By pushing pToken into thePurse with *pServerNym, I encrypt
11145  // it to pServerNym.
11146  // So now only the server Nym can decrypt that token and pop it
11147  // out of that purse.
11148  if (false ==
11149  pToken->ReassignOwnership(theNymAsOwner,
11150  theServerNymAsOwner)) {
11151  otErr << "Error re-assigning ownership of token (to "
11152  "server.)\n";
11153  bSuccess = false;
11154  break;
11155  }
11156  else {
11157  otLog3 << "Success re-assigning ownership of token (to "
11158  "server.)\n";
11159 
11160  bSuccess = true;
11161 
11162  pToken->ReleaseSignatures();
11163  pToken->SignContract(theNym);
11164  pToken->SaveContract();
11165 
11166  thePurse.Push(theServerNymAsOwner, *pToken);
11167 
11168  int64_t lTemp = pItem->GetAmount();
11169  pItem->SetAmount(lTemp + pToken->GetDenomination());
11170  }
11171  }
11172  else {
11173  otErr << "Error loading token from string.\n";
11174  }
11175  } // for
11176 
11177  if (bSuccess) {
11178  thePurse.SignContract(theNym);
11179  thePurse.SaveContract(); // I think this one is unnecessary.
11180 
11181  // Save the purse into a string...
11182  OTString strPurse;
11183  thePurse.SaveContractRaw(strPurse);
11184 
11185  // Add the purse string as the attachment on the transaction item.
11186  pItem->SetAttachment(
11187  strPurse); // The purse is contained in the reference string.
11188 
11189  // sign the item
11190  pItem->SignContract(theNym);
11191  pItem->SaveContract();
11192 
11193  // the Transaction "owns" the item now and will handle cleaning it
11194  // up.
11195  pTransaction->AddItem(*pItem); // the Transaction's destructor will
11196  // cleanup the item. It "owns" it
11197  // now.
11198 
11199  // BALANCE AGREEMENT
11200 
11201  // pBalanceItem is signed and saved within this call. No need to do
11202  // that again.
11203  OTItem* pBalanceItem = pInbox->GenerateBalanceStatement(
11204  pItem->GetAmount(), *pTransaction, theNym, *pAccount, *pOutbox);
11205 
11206  if (nullptr !=
11207  pBalanceItem) // will never be nullptr. Will assert above
11208  // before it gets here.
11209  pTransaction->AddItem(
11210  *pBalanceItem); // Better not be nullptr...
11211  // message will fail...
11212  // But better check
11213  // anyway.
11214 
11215  // sign the transaction
11216  pTransaction->SignContract(theNym);
11217  pTransaction->SaveContract();
11218 
11219  // set up the ledger
11220  OTLedger theLedger(USER_ID, ACCT_FROM_ID, SERVER_ID);
11221  theLedger.GenerateLedger(ACCT_FROM_ID, SERVER_ID,
11222  OTLedger::message); // bGenerateLedger
11223  // defaults to false,
11224  // which is correct.
11225  theLedger.AddTransaction(*pTransaction); // now the ledger "owns"
11226  // and will handle cleaning
11227  // up the transaction.
11228 
11229  // sign the ledger
11230  theLedger.SignContract(theNym);
11231  theLedger.SaveContract();
11232 
11233  // extract the ledger in ascii-armored form... encoding...
11234  OTString strLedger(theLedger);
11235  OTASCIIArmor ascLedger(strLedger); // I can't pass strLedger into
11236  // this constructor because I
11237  // want to encode it
11238 
11239  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
11240  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
11241  theMessage.m_strRequestNum.Format(
11242  "%lld", lRequestNumber); // Always have to send this.
11243  theNym.IncrementRequestNum(theNym, strServerID); // since I used it
11244  // for a server
11245  // request, I have
11246  // to increment it
11247 
11248  // (1) Set up member variables
11249  theMessage.m_strCommand = "notarizeTransactions";
11250  theMessage.m_strNymID = strNymID;
11251  theMessage.m_strServerID = strServerID;
11252  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
11253  // theMessage.m_strServerID
11254  // is already set. (It uses
11255  // it.)
11256 
11257  theMessage.m_strAcctID = strFromAcct;
11258  theMessage.m_ascPayload = ascLedger;
11259 
11260  OTIdentifier NYMBOX_HASH;
11261  const std::string str_server(strServerID.Get());
11262  const bool bNymboxHash =
11263  theNym.GetNymboxHash(str_server, NYMBOX_HASH);
11264 
11265  if (bNymboxHash)
11266  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
11267  else
11268  otErr << "Failed getting NymboxHash from Nym for server: "
11269  << str_server << "\n";
11270 
11271  // (2) Sign the Message
11272  theMessage.SignContract(theNym);
11273 
11274  // (3) Save the Message (with signatures and all, back to its
11275  // internal member m_strRawFile.)
11276  theMessage.SaveContract();
11277 
11278  bSendCommand = true;
11279  lReturnValue = lRequestNumber;
11280 
11281  } // bSuccess
11282  else {
11283  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
11284  // NUMBERS.
11285  theNym.AddTransactionNum(theNym, strServerID,
11286  lStoredTransactionNumber,
11287  true); // bSave=true
11288 
11289  delete pItem;
11290  pItem = nullptr;
11291  delete pTransaction;
11292  pTransaction = nullptr;
11293  }
11294  } break;
11295  case OTClient::notarizePurse: // NOTARIZE PURSE (deposit)
11296  {
11297  OTString strFromAcct;
11298 
11299  if (nullptr == pAccount) {
11300  otOut << "Please enter an asset Account ID: ";
11301  // User input.
11302  // I need a from account
11303  strFromAcct.OTfgets(std::cin);
11304 
11305  if (strFromAcct.GetLength() < 2) return (-1);
11306 
11307  const OTIdentifier ACCOUNT_ID(strFromAcct);
11308 
11309  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
11310  pAccount->GetIdentifier(strFromAcct);
11311  CONTRACT_ID = pAccount->GetAssetTypeID();
11312  CONTRACT_ID.GetString(strContractID);
11313  }
11314  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
11315  strFromAcct.Get())) != nullptr) {
11316  pAccount->GetIdentifier(strFromAcct);
11317  CONTRACT_ID = pAccount->GetAssetTypeID();
11318  CONTRACT_ID.GetString(strContractID);
11319  }
11320  else {
11321  otErr << "Unable to deposit without an account. Try adding: "
11322  "--myacct ACCOUNT_ID\n";
11323  return (-1);
11324  }
11325  }
11326  else {
11327  pAccount->GetIdentifier(strFromAcct);
11328  CONTRACT_ID = pAccount->GetAssetTypeID();
11329  CONTRACT_ID.GetString(strContractID);
11330  }
11331 
11332  if (pAccount->GetPurportedServerID() != SERVER_ID) {
11333  otErr << "OTClient::ProcessUserCommand: "
11334  "pAccount->GetPurportedServerID() doesn't match "
11335  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
11336  return (-1);
11337  }
11338 
11339  //
11340  // "from acct" is the acct we are depositing this cash to. aka MyAcct.
11341  const OTIdentifier ACCT_FROM_ID(strFromAcct), USER_ID(theNym);
11342 
11343  Purse thePurse(SERVER_ID, CONTRACT_ID);
11344 
11345  const OTPseudonym* pServerNym = theServer.GetContractPublicNym();
11346 
11347  Purse theSourcePurse(thePurse);
11348 
11349  OTString strAssetTypeID;
11350 
11351  // If no asset contract was passed in, then --mypurse was not specified.
11352  // Therefore,
11353  // we can get the purse from the user, and verify that it has the same
11354  // asset type ID
11355  // as pAccount does. (No need to ask for the type.)
11356  //
11357  // But if an asset contract WAS passed in, then we will assume (for now)
11358  // that the purse
11359  // from local storage will be used, of that same type, and thus no need
11360  // to ask for the type.
11361  //
11362  if (nullptr == pMyAssetContract) {
11363  OTString strSourcePurse;
11364 
11365  otOut << "Please enter a plaintext purse (of the same asset type "
11366  "as the account), \nand terminate with a ~ (tilde "
11367  "character) on a new line:\n> ";
11368  char decode_buffer[200]; // Safe since we only read
11369  // sizeof(decode_buffer)-1
11370 
11371  do {
11372  decode_buffer[0] = 0; // Make it fresh.
11373 
11374  if (!fgets(decode_buffer, sizeof(decode_buffer) - 1, stdin) &&
11375  (decode_buffer[0] != '~')) {
11376  strSourcePurse.Concatenate("%s", decode_buffer);
11377  otOut << "> ";
11378  }
11379  else {
11380  break;
11381  }
11382 
11383  } while (decode_buffer[0] != '~');
11384 
11385  if (false ==
11386  theSourcePurse.LoadContractFromString(strSourcePurse)) {
11387  otOut << "Failure trying to load purse from string provided by "
11388  "user.\n";
11389  return (-1);
11390  }
11391 
11392  // todo verify signature?
11393 
11394  theSourcePurse.GetAssetID().GetString(strAssetTypeID);
11395  }
11396  else {
11397  pMyAssetContract->GetIdentifier(strAssetTypeID);
11398 
11399  bool bLoadedSourcePurse = theSourcePurse.LoadPurse(
11400  strServerID.Get(), strNymID.Get(), strAssetTypeID.Get());
11401 
11402  if (!bLoadedSourcePurse) {
11403  otOut << "Deposit purse: Failure trying to load purse from "
11404  "local storage:\nServer " << strServerID << " Nym "
11405  << strNymID << " Asset Type " << strAssetTypeID << "\n";
11406  return (-1);
11407  }
11408  else
11409  otOut << "WARNING: This operation is very low-level. Once you "
11410  "deposit the purse in local storage,\nyou need to "
11411  "erase the purse file from local storage, since the "
11412  "tokens within it are\nall spent. (Otherwise, when "
11413  "you withdraw again, good tokens would be mixed in "
11414  "with\nthe spent ones, and then you'll have to sit "
11415  "there depositing them one-by-one, in order\nto sort "
11416  "it all out.\n (So just use the GUI and save yourself "
11417  "the trouble.)\n\nDeposit purse: using purse from "
11418  "local storage.\n Server " << strServerID << " Nym "
11419  << strNymID << " Asset Type " << strAssetTypeID << "\n";
11420 
11421  theSourcePurse.GetAssetID().GetString(strAssetTypeID);
11422  }
11423 
11424  // By this point, theSourcePurse is DEFINITELY good,
11425  // and strAssetTypeID contains its ID.
11426  const OTIdentifier ASSET_TYPE_ID(strAssetTypeID);
11427 
11428  if (ASSET_TYPE_ID != CONTRACT_ID) {
11429  otOut << "Asset ID on purse didn't match asset ID on account. "
11430  "\nTry: --myacct ACCT_ID (to specify a different "
11431  "account.)\nTo use the purse in local storage, try: "
11432  "--mypurse ASSET_TYPE_ID\nFYI, if you PREFER to provide "
11433  "the purse from user input, OT *will* ask you to\ninput a "
11434  "purse when doing this, just as long as --mypurse is NOT "
11435  "provided. (And\nthat includes the defaultmypurse value "
11436  "stored in ~/.ot/command-line-ot.opt)\n\n";
11437  return (-1);
11438  }
11439 
11440  // By this point, I have theSourcePurse loaded, whether from local
11441  // storage or from
11442  // the command-line, and I know that it has the same asset type as
11443  // pAccount.
11444  //
11445 
11446  int64_t lStoredTransactionNumber = 0;
11447  bool bGotTransNum = false;
11448 
11449  std::unique_ptr<OTLedger> pInbox(pAccount->LoadInbox(theNym));
11450  std::unique_ptr<OTLedger> pOutbox(pAccount->LoadOutbox(theNym));
11451 
11452  if (nullptr == pInbox) {
11453  otOut << "Failed loading inbox!\n";
11454  break;
11455  }
11456 
11457  if (nullptr == pOutbox) {
11458  otOut << "Failed loading outbox!\n";
11459  break;
11460  }
11461 
11462  bGotTransNum = theNym.GetNextTransactionNum(theNym, strServerID,
11463  lStoredTransactionNumber);
11464  if (!bGotTransNum) {
11465  otOut << "No Transaction Numbers were available. Try requesting "
11466  "the server for a new one.\n";
11467  break;
11468  }
11469 
11470  if (!pServerNym) {
11471  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
11472  // NUMBERS.
11473  theNym.AddTransactionNum(theNym, strServerID,
11474  lStoredTransactionNumber,
11475  true); // bSave=true
11476  break;
11477  }
11478 
11479  bool bSuccess = false;
11480 
11481  // Create a transaction
11483  USER_ID, ACCT_FROM_ID, SERVER_ID, OTTransaction::deposit,
11484  lStoredTransactionNumber);
11485 
11486  // set up the transaction item (each transaction may have multiple
11487  // items...)
11488  OTItem* pItem =
11490 
11491  OTString strNote("Deposit this cash, please!");
11492  pItem->SetNote(strNote);
11493 
11494  OTNym_or_SymmetricKey theNymAsOwner(theNym),
11495  theServerNymAsOwner(*pServerNym);
11496 
11497  while (!theSourcePurse.IsEmpty()) {
11498  std::unique_ptr<Token> pToken(theSourcePurse.Pop(theNym));
11499 
11500  if (pToken) {
11501  // TODO need 2-recipient envelopes. My request to the server is
11502  // encrypted to the server's nym,
11503  // but it should be encrypted to my Nym also, so both have
11504  // access to decrypt it.
11505  // TODO: AHH OR I could just put a copy of everything into a
11506  // SINGLE-recipient envelope made out to ME!!
11507  //
11508 
11509  // Now the token is ready, let's add it to a purse
11510  // By pushing theToken into thePurse with *pServerNym, I encrypt
11511  // it to pServerNym.
11512  // So now only the server Nym can decrypt that token and pop it
11513  // out of that purse.
11514  if (false ==
11515  pToken->ReassignOwnership(theNymAsOwner,
11516  theServerNymAsOwner)) {
11517  otErr << "Error re-assigning ownership of token (to "
11518  "server.)\n";
11519  bSuccess = false;
11520  break;
11521  }
11522  else {
11523  otLog3 << "Success re-assigning ownership of token (to "
11524  "server.)\n";
11525 
11526  bSuccess = true;
11527 
11528  pToken->ReleaseSignatures();
11529  pToken->SignContract(theNym);
11530  pToken->SaveContract();
11531 
11532  thePurse.Push(theServerNymAsOwner,
11533  *pToken); // <================
11534 
11535  int64_t lTemp = pItem->GetAmount();
11536  pItem->SetAmount(
11537  lTemp +
11538  pToken->GetDenomination()); // <==================
11539  }
11540  }
11541  else {
11542  otErr << "Error loading token from purse.\n";
11543  bSuccess = false;
11544  break;
11545  }
11546  }
11547 
11548  if (bSuccess) {
11549  thePurse.SignContract(theNym);
11550  thePurse.SaveContract(); // I think this one is unnecessary. UPDATE:
11551  // WRONG. It's necessary.
11552 
11553  // Save the purse into a string...
11554  OTString strPurse;
11555  thePurse.SaveContractRaw(strPurse);
11556 
11557  // Add the purse string as the attachment on the transaction item.
11558  pItem->SetAttachment(
11559  strPurse); // The purse is contained in the reference string.
11560 
11561  // sign the item
11562  pItem->SignContract(theNym);
11563  pItem->SaveContract();
11564 
11565  // the Transaction "owns" the item now and will handle cleaning it
11566  // up.
11567  pTransaction->AddItem(*pItem); // the Transaction's destructor will
11568  // cleanup the item. It "owns" it
11569  // now.
11570 
11571  // BALANCE AGREEMENT
11572 
11573  // pBalanceItem is signed and saved within this call. No need to do
11574  // that again.
11575  OTItem* pBalanceItem = pInbox->GenerateBalanceStatement(
11576  pItem->GetAmount(), *pTransaction, theNym, *pAccount, *pOutbox);
11577 
11578  if (nullptr !=
11579  pBalanceItem) // will never be nullptr. Will assert above
11580  // before it gets here.
11581  pTransaction->AddItem(
11582  *pBalanceItem); // Better not be nullptr...
11583  // message will fail...
11584  // But better check
11585  // anyway.
11586 
11587  // sign the transaction
11588  pTransaction->SignContract(theNym);
11589  pTransaction->SaveContract();
11590 
11591  // set up the ledger
11592  OTLedger theLedger(USER_ID, ACCT_FROM_ID, SERVER_ID);
11593  theLedger.GenerateLedger(ACCT_FROM_ID, SERVER_ID,
11594  OTLedger::message); // bGenerateLedger
11595  // defaults to false,
11596  // which is correct.
11597  theLedger.AddTransaction(*pTransaction); // now the ledger "owns"
11598  // and will handle cleaning
11599  // up the transaction.
11600 
11601  // sign the ledger
11602  theLedger.SignContract(theNym);
11603  theLedger.SaveContract();
11604 
11605  // extract the ledger in ascii-armored form... encoding...
11606  OTString strLedger(theLedger);
11607  OTASCIIArmor ascLedger(strLedger); // I can't pass strLedger into
11608  // this constructor because I
11609  // want to encode it
11610 
11611  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
11612  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
11613  theMessage.m_strRequestNum.Format(
11614  "%lld", lRequestNumber); // Always have to send this.
11615  theNym.IncrementRequestNum(theNym, strServerID); // since I used it
11616  // for a server
11617  // request, I have
11618  // to increment it
11619 
11620  // (1) Set up member variables
11621  theMessage.m_strCommand = "notarizeTransactions";
11622  theMessage.m_strNymID = strNymID;
11623  theMessage.m_strServerID = strServerID;
11624  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
11625  // theMessage.m_strServerID
11626  // is already set. (It uses
11627  // it.)
11628 
11629  theMessage.m_strAcctID = strFromAcct;
11630  theMessage.m_ascPayload = ascLedger;
11631 
11632  OTIdentifier NYMBOX_HASH;
11633  const std::string str_server(strServerID.Get());
11634  const bool bNymboxHash =
11635  theNym.GetNymboxHash(str_server, NYMBOX_HASH);
11636 
11637  if (bNymboxHash)
11638  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
11639  else
11640  otErr << "Failed getting NymboxHash from Nym for server: "
11641  << str_server << "\n";
11642 
11643  // (2) Sign the Message
11644  theMessage.SignContract(theNym);
11645 
11646  // (3) Save the Message (with signatures and all, back to its
11647  // internal member m_strRawFile.)
11648  theMessage.SaveContract();
11649 
11650  bSendCommand = true;
11651  lReturnValue = lRequestNumber;
11652  } // bSuccess
11653  else {
11654  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
11655  // NUMBERS.
11656  theNym.AddTransactionNum(theNym, strServerID,
11657  lStoredTransactionNumber,
11658  true); // bSave=true
11659 
11660  delete pItem;
11661  pItem = nullptr;
11662  delete pTransaction;
11663  pTransaction = nullptr;
11664  }
11665  } // else if (OTClient::notarizePurse == requestedCommand) // NOTARIZE PURSE
11666  break;
11667  case OTClient::notarizeCheque: // DEPOSIT CHEQUE
11668  {
11669  OTString strFromAcct;
11670 
11671  if (nullptr == pAccount) {
11672  otOut << "Please enter an asset Account ID (to deposit to): ";
11673  // User input.
11674  // I need a from account
11675  strFromAcct.OTfgets(std::cin);
11676 
11677  if (strFromAcct.GetLength() < 2) return (-1);
11678 
11679  const OTIdentifier ACCOUNT_ID(strFromAcct);
11680 
11681  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
11682  pAccount->GetIdentifier(strFromAcct);
11683  CONTRACT_ID = pAccount->GetAssetTypeID();
11684  CONTRACT_ID.GetString(strContractID);
11685  }
11686  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
11687  strFromAcct.Get())) != nullptr) {
11688  pAccount->GetIdentifier(strFromAcct);
11689  CONTRACT_ID = pAccount->GetAssetTypeID();
11690  CONTRACT_ID.GetString(strContractID);
11691  }
11692  else {
11693  otErr << "Unable to deposit without an account. Try adding: "
11694  "--myacct ACCOUNT_ID\n";
11695  return (-1);
11696  }
11697  }
11698  else {
11699  pAccount->GetIdentifier(strFromAcct);
11700  CONTRACT_ID = pAccount->GetAssetTypeID();
11701  CONTRACT_ID.GetString(strContractID);
11702  }
11703 
11704  if (pAccount->GetPurportedServerID() != SERVER_ID) {
11705  otErr << "OTClient::ProcessUserCommand: "
11706  "pAccount->GetPurportedServerID() doesn't match "
11707  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
11708  return (-1);
11709  }
11710 
11711  const OTIdentifier ACCT_FROM_ID(strFromAcct), USER_ID(theNym);
11712 
11713  OTCheque theCheque(SERVER_ID, CONTRACT_ID);
11714 
11715  otOut << "Please enter plaintext cheque, terminate with ~ on a new "
11716  "line:\n> ";
11717  OTString strCheque;
11718  char decode_buffer[200]; // Safe since we only read
11719  // sizeof(decode_buffer) - 1
11720 
11721  do {
11722  decode_buffer[0] = 0; // Make sure it's starting out fresh.
11723 
11724  if (!fgets(decode_buffer, sizeof(decode_buffer) - 1, stdin) &&
11725  (decode_buffer[0] != '~')) {
11726  strCheque.Concatenate("%s", decode_buffer);
11727  otOut << "> ";
11728  }
11729  else {
11730  break;
11731  }
11732 
11733  } while (decode_buffer[0] != '~');
11734 
11735  int64_t lStoredTransactionNumber = 0;
11736  bool bGotTransNum = theNym.GetNextTransactionNum(
11737  theNym, strServerID, lStoredTransactionNumber);
11738 
11739  if (!bGotTransNum) {
11740  otOut << "No Transaction Numbers were available. Try requesting "
11741  "the server for a new one.\n";
11742  }
11743  else if (theCheque.LoadContractFromString(strCheque)) {
11744  if (theCheque.HasRecipient() &&
11745  (theCheque.GetRecipientUserID() != USER_ID)) {
11746  const OTString strRecipientNym(theCheque.GetRecipientUserID());
11747  otOut << "This cheque is made out to the Nym: "
11748  << strRecipientNym
11749  << " (and that is NOT you, so you can't deposit it!)\n "
11750  "You are: " << strNymID << " \n";
11751  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
11752  // NUMBERS.
11753  theNym.AddTransactionNum(theNym, strServerID,
11754  lStoredTransactionNumber,
11755  true); // bSave=true
11756  }
11757  else // the cheque is blank, or is made out to me.
11758  {
11759  // Create a transaction
11760  OTTransaction* pTransaction =
11762  USER_ID, ACCT_FROM_ID, SERVER_ID,
11763  OTTransaction::deposit, lStoredTransactionNumber);
11764 
11765  // set up the transaction item (each transaction may have
11766  // multiple items...)
11768  *pTransaction, OTItem::depositCheque);
11769 
11770  OTString strNote("Deposit this cheque, please!");
11771  pItem->SetNote(strNote);
11772 
11773  strCheque.Release();
11774  theCheque.SaveContractRaw(strCheque);
11775 
11776  // Add the cheque string as the attachment on the transaction
11777  // item.
11778  pItem->SetAttachment(strCheque); // The cheque is contained in
11779  // the reference string.
11780 
11781  // sign the item
11782  pItem->SignContract(theNym);
11783  pItem->SaveContract();
11784 
11785  // the Transaction "owns" the item now and will handle cleaning
11786  // it up.
11787  pTransaction->AddItem(*pItem); // the Transaction's destructor
11788  // will cleanup the item. It
11789  // "owns" it now.
11790 
11791  std::unique_ptr<OTLedger> pInbox(pAccount->LoadInbox(theNym));
11792  std::unique_ptr<OTLedger> pOutbox(pAccount->LoadOutbox(theNym));
11793 
11794  if (nullptr == pInbox) {
11795  otOut << "Failed loading inbox!\n";
11796 
11797  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF
11798  // AVAILABLE NUMBERS.
11799  theNym.AddTransactionNum(theNym, strServerID,
11800  lStoredTransactionNumber,
11801  true); // bSave=true
11802  }
11803  else if (nullptr == pOutbox) {
11804  otOut << "Failed loading outbox!\n";
11805 
11806  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF
11807  // AVAILABLE NUMBERS.
11808  theNym.AddTransactionNum(theNym, strServerID,
11809  lStoredTransactionNumber,
11810  true); // bSave=true
11811  }
11812  else {
11813  // BALANCE AGREEMENT
11814 
11815  // pBalanceItem is signed and saved within this call. No
11816  // need to do that again.
11817  OTItem* pBalanceItem = pInbox->GenerateBalanceStatement(
11818  theCheque.GetAmount(), *pTransaction, theNym, *pAccount,
11819  *pOutbox);
11820 
11821  if (nullptr !=
11822  pBalanceItem) // will never be nullptr. Will assert
11823  // above before it gets here.
11824  pTransaction->AddItem(*pBalanceItem); // Better not be
11825  // nullptr... message
11826  // will fail...
11827  // But better
11828  // check anyway.
11829 
11830  // sign the transaction
11831  pTransaction->SignContract(theNym);
11832  pTransaction->SaveContract();
11833 
11834  // set up the ledger
11835  OTLedger theLedger(USER_ID, ACCT_FROM_ID, SERVER_ID);
11836  theLedger.GenerateLedger(
11837  ACCT_FROM_ID, SERVER_ID,
11838  OTLedger::message); // bGenerateLedger defaults to
11839  // false, which is correct.
11840  theLedger.AddTransaction(*pTransaction); // now the ledger
11841  // "owns" and will
11842  // handle cleaning
11843  // up the
11844  // transaction.
11845 
11846  // sign the ledger
11847  theLedger.SignContract(theNym);
11848  theLedger.SaveContract();
11849 
11850  // extract the ledger in ascii-armored form... encoding...
11851  OTString strLedger(theLedger);
11852  OTASCIIArmor ascLedger(strLedger);
11853 
11854  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
11855  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
11856  theMessage.m_strRequestNum.Format(
11857  "%lld", lRequestNumber); // Always have to send this.
11858  theNym.IncrementRequestNum(
11859  theNym, strServerID); // since I used it for a server
11860  // request, I have to increment it
11861 
11862  // (1) Set up member variables
11863  theMessage.m_strCommand = "notarizeTransactions";
11864  theMessage.m_strNymID = strNymID;
11865  theMessage.m_strServerID = strServerID;
11866  theMessage.SetAcknowledgments(
11867  theNym); // Must be called AFTER
11868  // theMessage.m_strServerID is already set. (It
11869  // uses it.)
11870 
11871  theMessage.m_strAcctID = strFromAcct;
11872  theMessage.m_ascPayload = ascLedger;
11873 
11874  OTIdentifier NYMBOX_HASH;
11875  const std::string str_server(strServerID.Get());
11876  const bool bNymboxHash =
11877  theNym.GetNymboxHash(str_server, NYMBOX_HASH);
11878 
11879  if (bNymboxHash)
11880  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
11881  else
11882  otErr
11883  << "Failed getting NymboxHash from Nym for server: "
11884  << str_server << "\n";
11885 
11886  // (2) Sign the Message
11887  theMessage.SignContract(theNym);
11888 
11889  // (3) Save the Message (with signatures and all, back to
11890  // its internal member m_strRawFile.)
11891  theMessage.SaveContract();
11892 
11893  bSendCommand = true;
11894  lReturnValue = lRequestNumber;
11895 
11896  } // inbox/outbox loaded
11897  } // the cheque is blank, or is made out to me
11898  } // cheque loaded
11899  else // cheque failed to load
11900  {
11901  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
11902  // NUMBERS.
11903  theNym.AddTransactionNum(theNym, strServerID,
11904  lStoredTransactionNumber,
11905  true); // bSave=true
11906  }
11907  } // else if (OTClient::notarizeCheque == requestedCommand) // DEPOSIT
11908  // CHEQUE
11909  break;
11910  case OTClient::withdrawVoucher: // WITHDRAW VOUCHER
11911  {
11912  OT_ASSERT(nullptr != m_pWallet);
11913  OTString strFromAcct;
11914 
11915  if (nullptr == pAccount) {
11916  otOut << "This is like a banker's cheque, aka cashier's "
11917  "cheque.\nPlease enter an asset Account ID (FROM acct): ";
11918  // User input.
11919  // I need a from account
11920  strFromAcct.OTfgets(std::cin);
11921 
11922  if (strFromAcct.GetLength() < 2) return (-1);
11923 
11924  const OTIdentifier ACCOUNT_ID(strFromAcct);
11925 
11926  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
11927  pAccount->GetIdentifier(strFromAcct);
11928  CONTRACT_ID = pAccount->GetAssetTypeID();
11929  CONTRACT_ID.GetString(strContractID);
11930  }
11931  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
11932  strFromAcct.Get())) != nullptr) {
11933  pAccount->GetIdentifier(strFromAcct);
11934  CONTRACT_ID = pAccount->GetAssetTypeID();
11935  CONTRACT_ID.GetString(strContractID);
11936  }
11937  else {
11938  otErr << "Unable to purchase voucher without a 'FROM' account. "
11939  "Try adding: --myacct ACCOUNT_ID\n";
11940  return (-1);
11941  }
11942  }
11943  else {
11944  pAccount->GetIdentifier(strFromAcct);
11945  CONTRACT_ID = pAccount->GetAssetTypeID();
11946  CONTRACT_ID.GetString(strContractID);
11947  }
11948 
11949  const OTIdentifier ACCOUNT_ID(strFromAcct);
11950 
11951  if (pAccount->GetPurportedServerID() != SERVER_ID) {
11952  otErr << "OTClient::ProcessUserCommand: "
11953  "pAccount->GetPurportedServerID() doesn't match "
11954  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
11955  return (-1);
11956  }
11957  OTString strRecipientNym;
11958 
11959  if (nullptr == pHisNymID) {
11960  otOut << "Enter Recipient's Nym ID (full ID -- no partials here.) "
11961  "Blank IS allowed: ";
11962 
11963  // User input.
11964  // I need a from account
11965  strRecipientNym.OTfgets(std::cin);
11966 
11967  // if (strRecipientNym.GetLength() < 2) // blank cheques
11968  // are allowed.
11969  // return (-1);
11970  }
11971  else {
11972  pHisNymID->GetString(strRecipientNym);
11973  }
11974 
11975  // Todo add partial lookups here from wallet and/or address book.
11976 
11977  const OTIdentifier MY_NYM_ID(theNym);
11978  const OTIdentifier HIS_NYM_ID(strRecipientNym);
11979 
11980  OTString strAmount;
11981  if (0 == lTransactionAmount) {
11982  otOut << "Please enter an amount: ";
11983  // User input.
11984  // I need an amount
11985  strAmount.OTfgets(std::cin);
11986  }
11987 
11988  const int64_t lTotalAmount =
11989  (0 == lTransactionAmount)
11990  ? // If nothing was passed in, then use atol(strAmount),
11991  (atol(strAmount.Exists() ? strAmount.Get() : "0"))
11992  : lTransactionAmount; // otherwise lTransactionAmount.
11993  int64_t lWithdrawTransNum = 0, lVoucherTransNum = 0;
11994 
11995  bool bGotTransNum1 = theNym.GetNextTransactionNum(theNym, strServerID,
11996  lWithdrawTransNum);
11997  bool bGotTransNum2 =
11998  theNym.GetNextTransactionNum(theNym, strServerID, lVoucherTransNum);
11999 
12000  if (!bGotTransNum1 || !bGotTransNum2) {
12001  otOut << __FUNCTION__ << ": Not enough Transaction Numbers were "
12002  "available. (Suggest requesting the "
12003  "server for more.)\n";
12004 
12005  if (bGotTransNum1)
12006  theNym.AddTransactionNum(theNym, strServerID, lWithdrawTransNum,
12007  true); // bSave=true
12008  if (bGotTransNum2)
12009  theNym.AddTransactionNum(theNym, strServerID, lVoucherTransNum,
12010  true); // bSave=true
12011  }
12012  else {
12013  // Memo
12014  otOut << "Enter a memo for your check: ";
12015  OTString strChequeMemo;
12016  strChequeMemo.OTfgets(std::cin);
12017  // Expiration (ignored by server -- it sets its own for its
12018  // vouchers.)
12019  const time64_t VALID_FROM =
12020  OTTimeGetCurrentTime(); // This time is set to TODAY NOW
12021  const time64_t VALID_TO = OTTimeAddTimeInterval(
12022  VALID_FROM, OTTimeGetSecondsFromTime(
12023  OT_TIME_SIX_MONTHS_IN_SECONDS)); // 6 months.
12024  // The server only uses the memo, amount, and recipient from this
12025  // cheque when it
12026  // constructs the actual voucher.
12027  OTCheque theRequestVoucher(SERVER_ID, CONTRACT_ID);
12028  bool bIssueCheque = theRequestVoucher.IssueCheque(
12029  lTotalAmount, lVoucherTransNum, VALID_FROM, VALID_TO,
12030  ACCOUNT_ID, MY_NYM_ID, strChequeMemo,
12031  (strRecipientNym.GetLength() > 2) ? &(HIS_NYM_ID) : nullptr);
12032 
12033  std::unique_ptr<OTLedger> pInbox(pAccount->LoadInbox(theNym));
12034  std::unique_ptr<OTLedger> pOutbox(pAccount->LoadOutbox(theNym));
12035 
12036  if (nullptr == pInbox) {
12037  otOut << "Failed loading inbox!\n";
12038 
12039  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
12040  // NUMBERS.
12041  theNym.AddTransactionNum(theNym, strServerID, lWithdrawTransNum,
12042  true); // bSave=true
12043  theNym.AddTransactionNum(theNym, strServerID, lVoucherTransNum,
12044  true); // bSave=true
12045  }
12046  else if (nullptr == pOutbox) {
12047  otOut << "Failed loading outbox!\n";
12048 
12049  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
12050  // NUMBERS.
12051  theNym.AddTransactionNum(theNym, strServerID, lWithdrawTransNum,
12052  true); // bSave=true
12053  theNym.AddTransactionNum(theNym, strServerID, lVoucherTransNum,
12054  true); // bSave=true
12055  }
12056  else if (bIssueCheque) {
12057  // Create a transaction
12058  OTTransaction* pTransaction =
12060  MY_NYM_ID, ACCOUNT_ID, SERVER_ID,
12061  OTTransaction::withdrawal, lWithdrawTransNum);
12062 
12063  // set up the transaction item (each transaction may have
12064  // multiple items...)
12066  *pTransaction, OTItem::withdrawVoucher);
12067  pItem->SetAmount(lTotalAmount);
12068  OTString strNote("Withdraw Voucher: ");
12069  pItem->SetNote(strNote);
12070 
12071  // Add the voucher request string as the attachment on the
12072  // transaction item.
12073  OTString strVoucher;
12074  theRequestVoucher.SignContract(theNym);
12075  theRequestVoucher.SaveContract();
12076  theRequestVoucher.SaveContractRaw(strVoucher);
12077  pItem->SetAttachment(strVoucher); // The voucher request is
12078  // contained in the reference
12079  // string.
12080 
12081  // sign the item
12082  pItem->SignContract(theNym);
12083  pItem->SaveContract();
12084 
12085  pTransaction->AddItem(*pItem); // the Transaction's destructor
12086  // will cleanup the item. It
12087  // "owns" it now.
12088  // BALANCE AGREEMENT
12089 
12090  // The item is signed and saved within this call as well. No
12091  // need to do that again.
12092  OTItem* pBalanceItem = pInbox->GenerateBalanceStatement(
12093  lTotalAmount * (-1), *pTransaction, theNym, *pAccount,
12094  *pOutbox);
12095 
12096  if (nullptr != pBalanceItem)
12097  pTransaction->AddItem(*pBalanceItem); // Better not be
12098  // nullptr... message
12099  // will fail... But
12100  // better check
12101  // anyway.
12102  // sign the transaction
12103  pTransaction->SignContract(theNym);
12104  pTransaction->SaveContract();
12105 
12106  // set up the ledger
12107  OTLedger theLedger(MY_NYM_ID, ACCOUNT_ID, SERVER_ID);
12108  theLedger.GenerateLedger(ACCOUNT_ID, SERVER_ID,
12109  OTLedger::message); // bGenerateLedger
12110  // defaults to
12111  // false, which is
12112  // correct.
12113  theLedger.AddTransaction(*pTransaction);
12114 
12115  // sign the ledger
12116  theLedger.SignContract(theNym);
12117  theLedger.SaveContract();
12118 
12119  // extract the ledger in ascii-armored form
12120  OTString strLedger(theLedger);
12121  OTASCIIArmor ascLedger; // I can't pass strLedger into this
12122  // constructor because I want to encode
12123  // it
12124 
12125  // Encoding...
12126  ascLedger.SetString(strLedger);
12127 
12128  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
12129  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
12130  theMessage.m_strRequestNum.Format(
12131  "%lld", lRequestNumber); // Always have to send this.
12132  theNym.IncrementRequestNum(
12133  theNym, strServerID); // since I used it for a server
12134  // request, I have to increment it
12135 
12136  // (1) Set up member variables
12137  theMessage.m_strCommand = "notarizeTransactions";
12138  theMessage.m_strNymID = strNymID;
12139  theMessage.m_strServerID = strServerID;
12140  theMessage.SetAcknowledgments(
12141  theNym); // Must be called AFTER theMessage.m_strServerID is
12142  // already set. (It uses it.)
12143 
12144  theMessage.m_strAcctID = strFromAcct;
12145  theMessage.m_ascPayload = ascLedger;
12146 
12147  OTIdentifier NYMBOX_HASH;
12148  const std::string str_server(strServerID.Get());
12149  const bool bNymboxHash =
12150  theNym.GetNymboxHash(str_server, NYMBOX_HASH);
12151 
12152  if (bNymboxHash)
12153  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
12154  else
12155  otErr << "Failed getting NymboxHash from Nym for server: "
12156  << str_server << "\n";
12157 
12158  // (2) Sign the Message
12159  theMessage.SignContract(theNym);
12160 
12161  // (3) Save the Message (with signatures and all, back to its
12162  // internal member m_strRawFile.)
12163  theMessage.SaveContract();
12164 
12165  bSendCommand = true;
12166  lReturnValue = lRequestNumber;
12167  }
12168  else {
12169  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
12170  // NUMBERS.
12171  theNym.AddTransactionNum(theNym, strServerID, lWithdrawTransNum,
12172  true); // bSave=true
12173  theNym.AddTransactionNum(theNym, strServerID, lVoucherTransNum,
12174  true); // bSave=true
12175  }
12176  }
12177  }
12178 
12179  /*
12180  bool ProcessUserCommand(OT_CLIENT_CMD_TYPE requestedCommand, OTMessage&
12181  theMessage, OTPseudonym& theNym,
12182  // OTAssetContract& theContract,
12183  OTServerContract& theServer,
12184  OTAccount * pAccount=nullptr,
12185  int64_t lTransactionAmount = 0,
12186  OTAssetContract * pMyAssetContract=nullptr,
12187  OTAccount * pHisAcct=nullptr,
12188  OTPseudonym * pHisNym=nullptr);
12189  */
12190  break;
12191  case OTClient::notarizeWithdrawal: // NOTARIZE WITHDRAWAL
12192  {
12193  OTString strFromAcct;
12194 
12195  if (nullptr == pAccount) {
12196  otOut << "Please enter an Asset Account ID: ";
12197  // User input.
12198  // I need a from account
12199  strFromAcct.OTfgets(std::cin);
12200 
12201  if (strFromAcct.GetLength() < 2) return (-1);
12202 
12203  const OTIdentifier ACCOUNT_ID(strFromAcct);
12204 
12205  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
12206  pAccount->GetIdentifier(strFromAcct);
12207  CONTRACT_ID = pAccount->GetAssetTypeID();
12208  CONTRACT_ID.GetString(strContractID);
12209  }
12210  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
12211  strFromAcct.Get())) != nullptr) {
12212  pAccount->GetIdentifier(strFromAcct);
12213  CONTRACT_ID = pAccount->GetAssetTypeID();
12214  CONTRACT_ID.GetString(strContractID);
12215  }
12216  else {
12217  otErr << "Unable to withdraw without account. Try adding: "
12218  "--myacct ACCOUNT_ID\n";
12219  return (-1);
12220  }
12221  }
12222  else {
12223  pAccount->GetIdentifier(strFromAcct);
12224  CONTRACT_ID = pAccount->GetAssetTypeID();
12225  CONTRACT_ID.GetString(strContractID);
12226  }
12227 
12228  if (pAccount->GetPurportedServerID() != SERVER_ID) {
12229  otErr << "OTClient::ProcessUserCommand: "
12230  "pAccount->GetPurportedServerID() doesn't match "
12231  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
12232  return (-1);
12233  }
12234 
12235  OTString strAmount;
12236  if (0 == lTransactionAmount) {
12237  otOut << "Please enter an amount: ";
12238  // User input.
12239  // I need an amount
12240  strAmount.OTfgets(std::cin);
12241  }
12242 
12243  const int64_t lTotalAmount =
12244  (0 == lTransactionAmount)
12245  ? // If nothing was passed in, then use atol(strAmount),
12246  (atol(strAmount.Exists() ? strAmount.Get() : "0"))
12247  : lTransactionAmount; // otherwise lTransactionAmount.
12248  int64_t lAmount = lTotalAmount; // Used in calculating the denominations
12249  // of tokens needed for the withdrawal.
12250 
12251  const OTIdentifier ACCT_FROM_ID(strFromAcct), USER_ID(theNym);
12252 
12253  int64_t lStoredTransactionNumber = 0;
12254  bool bGotTransNum = false;
12255 
12256  std::unique_ptr<OTLedger> pInbox(pAccount->LoadInbox(theNym));
12257  std::unique_ptr<OTLedger> pOutbox(pAccount->LoadOutbox(theNym));
12258 
12259  if (nullptr == pInbox) {
12260  otOut << "Failed loading inbox!\n";
12261  break;
12262  }
12263  else if (nullptr == pOutbox) {
12264  otOut << "Failed loading outbox!\n";
12265  break;
12266  }
12267 
12268  bGotTransNum = theNym.GetNextTransactionNum(theNym, strServerID,
12269  lStoredTransactionNumber);
12270  if (!bGotTransNum) {
12271  otOut << "No Transaction Numbers were available. Suggest "
12272  "requesting the server for a new one.\n";
12273  break;
12274  }
12275 
12276  // Create a transaction
12278  USER_ID, ACCT_FROM_ID, SERVER_ID, OTTransaction::withdrawal,
12279  lStoredTransactionNumber);
12280 
12281  // set up the transaction item (each transaction may have multiple
12282  // items...)
12283  OTItem* pItem = OTItem::CreateItemFromTransaction(*pTransaction,
12285  pItem->SetAmount(lTotalAmount);
12286  OTString strNote("Gimme cash!");
12287  pItem->SetNote(strNote);
12288 
12289  const OTPseudonym* pServerNym = theServer.GetContractPublicNym();
12290  std::unique_ptr<Mint> pMint(
12291  Mint::MintFactory(strServerID, strContractID));
12292  OT_ASSERT(nullptr != pMint);
12293  if (pServerNym && pMint->LoadMint() &&
12294  pMint->VerifyMint((OTPseudonym&)*pServerNym)) {
12295  Purse* pPurse = new Purse(SERVER_ID, CONTRACT_ID);
12296  Purse* pPurseMyCopy = new Purse(SERVER_ID, CONTRACT_ID);
12297 
12298  // Create all the necessary tokens for the withdrawal amount.
12299  // Push copies of each token into a purse to be sent to the server,
12300  // as well as a purse to be kept for unblinding when we receive the
12301  // server response. (Coin private unblinding keys are not sent to
12302  // the server, obviously.)
12303  int64_t lTokenAmount = 0;
12304  while ((lTokenAmount = pMint->GetLargestDenomination(lAmount)) >
12305  0) {
12306  lAmount -= lTokenAmount;
12307 
12308  // Create the relevant token request with same server/asset ID
12309  // as the purse.
12310  // the purse does NOT own the token at this point. the token's
12311  // constructor
12312  // just uses it to copy some IDs, since they must match.
12313  std::unique_ptr<Token> pToken(
12315  *pPurse, theNym, *pMint, lTokenAmount));
12316  OT_ASSERT(nullptr != pToken);
12317 
12318  // GENERATE new token, sign it and save it.
12319  pToken->SignContract(theNym);
12320  pToken->SaveContract();
12321 
12322  // Now the proto-token is generated, let's add it to a purse
12323  // By pushing pToken into pPurse with *pServerNym, I encrypt it
12324  // to pServerNym.
12325  // So now only the server Nym can decrypt that token and pop it
12326  // out of that purse.
12327  pPurse->Push(*pServerNym, *pToken);
12328 
12329  // I'm saving my own copy of all this, encrypted to my nym
12330  // instead of the server's, so I can get to my private coin
12331  // data.
12332  // The server's copy of pToken is already Pushed, so I can
12333  // re-use
12334  // the variable now for my own purse.
12335  pToken->ReleaseSignatures();
12336  pToken->SetSavePrivateKeys(); // This time it will save the
12337  // private keys when I sign it
12338  pToken->SignContract(theNym);
12339  pToken->SaveContract();
12340 
12341  pPurseMyCopy->Push(theNym, *pToken); // Now my copy of the purse
12342  // has a version of the
12343  // token,
12344  }
12345 
12346  pPurse->SignContract(theNym);
12347  pPurse->SaveContract(); // I think this one is unnecessary.
12348 
12349  // Save the purse into a string...
12350  OTString strPurse;
12351  pPurse->SaveContractRaw(strPurse);
12352 
12353  // Add the purse string as the attachment on the transaction item.
12354  pItem->SetAttachment(
12355  strPurse); // The purse is contained in the reference string.
12356 
12357  pPurseMyCopy->SignContract(
12358  theNym); // encrypted to me instead of the server, and including
12359  pPurseMyCopy->SaveContract(); // the private keys for unblinding the
12360  // server response.
12361  // This thing is neat and tidy. The wallet can just save it as an
12362  // ascii-armored string as a
12363  // purse field inside the wallet file. It doesn't do that for now
12364  // (TODO) but it easily could.
12365 
12366  // Add the purse to the wallet
12367  // (We will need it to look up the private coin info for unblinding
12368  // the token,
12369  // when the response comes from the server.)
12370  m_pWallet->AddPendingWithdrawal(*pPurseMyCopy);
12371 
12372  delete pPurse;
12373  pPurse = nullptr; // We're done with this one.
12374  pPurseMyCopy =
12375  nullptr; // The wallet owns my copy now and will handle
12376  // cleaning it up.
12377 
12378  // sign the item
12379  pItem->SignContract(theNym);
12380  pItem->SaveContract();
12381 
12382  pTransaction->AddItem(*pItem); // the Transaction's destructor will
12383  // cleanup the item. It "owns" it
12384  // now.
12385 
12386  // BALANCE AGREEMENT
12387 
12388  // pBalanceItem is signed and saved within this call. No need to do
12389  // that again.
12390  OTItem* pBalanceItem = pInbox->GenerateBalanceStatement(
12391  lTotalAmount * (-1), *pTransaction, theNym, *pAccount,
12392  *pOutbox);
12393 
12394  if (nullptr !=
12395  pBalanceItem) // will never be nullptr. Will assert above
12396  // before it gets here.
12397  pTransaction->AddItem(
12398  *pBalanceItem); // Better not be nullptr...
12399  // message will fail...
12400  // But better check
12401  // anyway.
12402 
12403  // sign the transaction
12404  pTransaction->SignContract(theNym);
12405  pTransaction->SaveContract();
12406 
12407  // set up the ledger
12408  OTLedger theLedger(USER_ID, ACCT_FROM_ID, SERVER_ID);
12409  theLedger.GenerateLedger(ACCT_FROM_ID, SERVER_ID,
12410  OTLedger::message); // bGenerateLedger
12411  // defaults to false,
12412  // which is correct.
12413  theLedger.AddTransaction(*pTransaction);
12414 
12415  // sign the ledger
12416  theLedger.SignContract(theNym);
12417  theLedger.SaveContract();
12418 
12419  // extract the ledger in ascii-armored form
12420  OTString strLedger(theLedger);
12421  OTASCIIArmor ascLedger; // I can't pass strLedger into this
12422  // constructor because I want to encode it
12423 
12424  // Encoding...
12425  ascLedger.SetString(strLedger);
12426 
12427  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
12428  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
12429  theMessage.m_strRequestNum.Format(
12430  "%lld", lRequestNumber); // Always have to send this.
12431  theNym.IncrementRequestNum(theNym, strServerID); // since I used it
12432  // for a server
12433  // request, I have
12434  // to increment it
12435 
12436  // (1) Set up member variables
12437  theMessage.m_strCommand = "notarizeTransactions";
12438  theMessage.m_strNymID = strNymID;
12439  theMessage.m_strServerID = strServerID;
12440  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
12441  // theMessage.m_strServerID
12442  // is already set. (It uses
12443  // it.)
12444 
12445  theMessage.m_strAcctID = strFromAcct;
12446  theMessage.m_ascPayload = ascLedger;
12447 
12448  OTIdentifier NYMBOX_HASH;
12449  const std::string str_server(strServerID.Get());
12450  const bool bNymboxHash =
12451  theNym.GetNymboxHash(str_server, NYMBOX_HASH);
12452 
12453  if (bNymboxHash)
12454  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
12455  else
12456  otErr << "Failed getting NymboxHash from Nym for server: "
12457  << str_server << "\n";
12458 
12459  // (2) Sign the Message
12460  theMessage.SignContract(theNym);
12461 
12462  // (3) Save the Message (with signatures and all, back to its
12463  // internal member m_strRawFile.)
12464  theMessage.SaveContract();
12465 
12466  bSendCommand = true;
12467  lReturnValue = lRequestNumber;
12468  }
12469  else {
12470  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
12471  // NUMBERS.
12472  theNym.AddTransactionNum(theNym, strServerID,
12473  lStoredTransactionNumber,
12474  true); // bSave=true
12475  }
12476 
12477  } break;
12478  case OTClient::getTransactionNum: // GET TRANSACTION NUM
12479  {
12480  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
12481  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
12482  theMessage.m_strRequestNum.Format(
12483  "%lld", lRequestNumber); // Always have to send this.
12484  theNym.IncrementRequestNum(theNym, strServerID); // since I used it for
12485  // a server request, I
12486  // have to increment it
12487 
12488  // (1) Set up member variables
12489  theMessage.m_strCommand = "getTransactionNum";
12490  theMessage.m_strNymID = strNymID;
12491  theMessage.m_strServerID = strServerID;
12492  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
12493  // theMessage.m_strServerID is
12494  // already set. (It uses it.)
12495 
12496  OTIdentifier NYMBOX_HASH;
12497  const std::string str_server(strServerID.Get());
12498  const bool bNymboxHash = theNym.GetNymboxHash(str_server, NYMBOX_HASH);
12499 
12500  if (bNymboxHash)
12501  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
12502  else
12503  otErr << "Failed getting NymboxHash from Nym for server: "
12504  << str_server << "\n";
12505 
12506  // (2) Sign the Message
12507  theMessage.SignContract(theNym);
12508 
12509  // (3) Save the Message (with signatures and all, back to its internal
12510  // member m_strRawFile.)
12511  theMessage.SaveContract();
12512 
12513  bSendCommand = true;
12514  lReturnValue = lRequestNumber;
12515  } break;
12516  case OTClient::marketOffer: // PUT AN OFFER ON A MARKET
12517  {
12518  if (theNym.GetTransactionNumCount(strServerID) < 3) {
12519  otOut << "You need at least 3 transaction numbers to do this (You "
12520  "don't have enough.)\n";
12521  }
12522  else {
12523  int64_t lStoredTransactionNumber = 0,
12524  lClosingTransactionNoAssetAcct = 0,
12525  lClosingTransactionNoCurrencyAcct = 0;
12526  bool bGotTransNum = theNym.GetNextTransactionNum(
12527  theNym, strServerID, lStoredTransactionNumber, false);
12528  bool bGotClosingNumAssetAcct = theNym.GetNextTransactionNum(
12529  theNym, strServerID, lClosingTransactionNoAssetAcct, false);
12530  bool bGotClosingNumCurrencyAcct = theNym.GetNextTransactionNum(
12531  theNym, strServerID, lClosingTransactionNoCurrencyAcct, true);
12532 
12533  if (!bGotTransNum || !bGotClosingNumAssetAcct ||
12534  !bGotClosingNumCurrencyAcct) {
12535  otOut << "Strange... had enough transcation numbers, but error "
12536  "trying to get one (or both.)\n";
12537 
12538  if (bGotTransNum)
12539  theNym.AddTransactionNum(theNym, strServerID,
12540  lStoredTransactionNumber, false);
12541 
12542  if (bGotClosingNumAssetAcct)
12543  theNym.AddTransactionNum(theNym, strServerID,
12544  lClosingTransactionNoAssetAcct,
12545  false);
12546 
12547  if (bGotClosingNumCurrencyAcct)
12548  theNym.AddTransactionNum(theNym, strServerID,
12549  lClosingTransactionNoCurrencyAcct,
12550  false);
12551 
12552  if (bGotTransNum || bGotClosingNumAssetAcct ||
12553  bGotClosingNumCurrencyAcct)
12554  theNym.SaveSignedNymfile(theNym);
12555  }
12556  else {
12557  OTString str_ASSET_TYPE_ID, str_CURRENCY_TYPE_ID,
12558  str_ASSET_ACCT_ID, str_CURRENCY_ACCT_ID;
12559 
12560  // FIRST get the Asset Type ID
12561  otOut << "Enter the Asset Type ID of the market you want to "
12562  "trade in: ";
12563  str_ASSET_TYPE_ID.OTfgets(std::cin);
12564 
12565  // THEN GET AN ACCOUNT ID FOR THAT ASSET TYPE
12566  otOut << "Enter an ACCOUNT ID of yours for an account of the "
12567  "same asset type: ";
12568  str_ASSET_ACCT_ID.OTfgets(std::cin);
12569 
12570  // NEXT get the Currency Type ID (which is also an asset type
12571  // ID, FYI.)
12572  // The trader just chooses one of them to be the "asset" and the
12573  // other, the "currency".
12574  otOut << "Enter the Currency Type ID of the market you want to "
12575  "trade in: ";
12576  str_CURRENCY_TYPE_ID.OTfgets(std::cin);
12577 
12578  // THEN GET AN ACCOUNT ID FOR THAT CURRENCY TYPE
12579  otOut << "Enter an ACCOUNT ID of yours, for an account of that "
12580  "same currency type: ";
12581  str_CURRENCY_ACCT_ID.OTfgets(std::cin);
12582 
12583  // Get a few int64_t integers that we need...
12584 
12585  OTString strTemp;
12586  int64_t lTotalAssetsOnOffer = 0, lMinimumIncrement = 0,
12587  lPriceLimit = 0;
12588 
12589  otOut << "What is the market granularity (or 'scale')? [1]: ";
12590  strTemp.Release();
12591  strTemp.OTfgets(std::cin);
12592  int64_t lMarketScale = atol(strTemp.Get());
12593 
12594  if (lMarketScale < 1) lMarketScale = 1;
12595 
12596  otOut << "What is the minimum increment per trade? (will be "
12597  "multiplied by the scale) [1]: ";
12598  strTemp.Release();
12599  strTemp.OTfgets(std::cin);
12600  lMinimumIncrement = atol(strTemp.Get());
12601 
12602  lMinimumIncrement *= lMarketScale;
12603 
12604  // In case they entered 0.
12605  if (lMinimumIncrement < 1) lMinimumIncrement = lMarketScale;
12606 
12607  otOut << "How many assets total do you have available for sale "
12608  "or purchase?\n(Will be multiplied by minimum "
12609  "increment) [1]: ";
12610  strTemp.Release();
12611  strTemp.OTfgets(std::cin);
12612  lTotalAssetsOnOffer = atol(strTemp.Get());
12613 
12614  // lTotalAssetsOnOffer *= lMinimumIncrement; //
12615  // this was a bug.
12616 
12617  if (lTotalAssetsOnOffer < 1)
12618  lTotalAssetsOnOffer = lMinimumIncrement;
12619 
12620  for (;;) {
12621  otOut << "The Market Scale is: " << lMarketScale
12622  << "\nWhat is your price limit, in currency, PER "
12623  "SCALE of assets?\nThat is, what is the lowest "
12624  "amount of currency you'd sell for, (if "
12625  "selling)\nOr the highest amount you'd pay (if "
12626  "you are buying).\nAgain, PER SCALE: ";
12627  strTemp.Release();
12628  strTemp.OTfgets(std::cin);
12629  lPriceLimit = atol(strTemp.Get());
12630 
12631  if (lPriceLimit < 1)
12632  otOut << "Price must be at least 1.\n\n";
12633  else
12634  break;
12635  }
12636 
12637  // which direction is the offer? Buy or sell?
12638  bool bBuyingOrSelling;
12639  OTString strDirection;
12640  otOut << "Are you in the market to buy the asset type, or to "
12641  "sell? [buy]: ";
12642  strDirection.OTfgets(std::cin);
12643 
12644  if (strDirection.Compare("sell") ||
12645  strDirection.Compare("Sell"))
12646  bBuyingOrSelling = true;
12647  else
12648  bBuyingOrSelling = false;
12649 
12650  OTIdentifier USER_ID(strNymID),
12651  ASSET_TYPE_ID(str_ASSET_TYPE_ID),
12652  CURRENCY_TYPE_ID(str_CURRENCY_TYPE_ID),
12653  ASSET_ACCT_ID(str_ASSET_ACCT_ID),
12654  CURRENCY_ACCT_ID(str_CURRENCY_ACCT_ID);
12655 
12656  OTOffer theOffer(SERVER_ID, ASSET_TYPE_ID, CURRENCY_TYPE_ID,
12657  lMarketScale);
12658 
12659  bool bCreateOffer = theOffer.MakeOffer(
12660  bBuyingOrSelling, // True == SELLING, False == BUYING
12661  lPriceLimit, // Per Minimum Increment...
12662  lTotalAssetsOnOffer, // Total assets available for sale or
12663  // purchase.
12664  lMinimumIncrement, // The minimum increment that must be
12665  // bought or sold for each transaction
12666  lStoredTransactionNumber); // Transaction number matches on
12667  // transaction, item, offer, and
12668  // trade.
12669 
12670  if (bCreateOffer) {
12671  bCreateOffer = theOffer.SignContract(theNym);
12672 
12673  if (bCreateOffer) bCreateOffer = theOffer.SaveContract();
12674  }
12675 
12676  OTTrade theTrade(SERVER_ID, ASSET_TYPE_ID, ASSET_ACCT_ID,
12677  USER_ID, CURRENCY_TYPE_ID, CURRENCY_ACCT_ID);
12678 
12679  bool bIssueTrade = theTrade.IssueTrade(theOffer);
12680 
12681  if (bIssueTrade) {
12682  theTrade.AddClosingTransactionNo(
12683  lClosingTransactionNoAssetAcct);
12684  theTrade.AddClosingTransactionNo(
12685  lClosingTransactionNoCurrencyAcct);
12686 
12687  bIssueTrade = theTrade.SignContract(theNym);
12688 
12689  if (bIssueTrade) bIssueTrade = theTrade.SaveContract();
12690  }
12691 
12692  if (bCreateOffer && bIssueTrade) {
12693  // Create a transaction
12694  OTTransaction* pTransaction =
12696  USER_ID, ASSET_ACCT_ID, SERVER_ID,
12698  lStoredTransactionNumber);
12699 
12700  // set up the transaction item (each transaction may have
12701  // multiple items...)
12703  *pTransaction, OTItem::marketOffer,
12704  &CURRENCY_ACCT_ID); // the "To" account (normally used
12705  // for a TRANSFER transaction) is
12706  // used here
12707  // storing the Currency Acct ID. The Server will expect the
12708  // Trade object bundled
12709  // within this item to have an Asset Acct ID and "Currency"
12710  // Acct ID that match
12711  // those on this Item. Otherwise it will reject the offer.
12712 
12713  OT_ASSERT(nullptr != pItem);
12714 
12715  OTString strTrade;
12716  theTrade.SaveContractRaw(strTrade);
12717 
12718  // Add the trade string as the attachment on the transaction
12719  // item.
12720  pItem->SetAttachment(strTrade); // The trade is contained in
12721  // the attachment string.
12722  // (The offer is within the
12723  // trade.)
12724 
12725  // sign the item
12726  pItem->SignContract(theNym);
12727  pItem->SaveContract();
12728 
12729  // the Transaction "owns" the item now and will handle
12730  // cleaning it up.
12731  pTransaction->AddItem(*pItem); // the Transaction's
12732  // destructor will cleanup
12733  // the item. It "owns" it
12734  // now.
12735 
12736  // TRANSACTION AGREEMENT
12737 
12738  // pBalanceItem is signed and saved within this call. No
12739  // need to do that again.
12740  OTItem* pStatementItem =
12741  theNym.GenerateTransactionStatement(*pTransaction);
12742 
12743  if (nullptr !=
12744  pStatementItem) // will never be nullptr. Will
12745  // assert above before it gets
12746  // here.
12747  pTransaction->AddItem(*pStatementItem); // Better not be
12748  // nullptr...
12749  // message will
12750  // fail... But
12751  // better check
12752  // anyway.
12753 
12754  // sign the transaction
12755  pTransaction->SignContract(theNym);
12756  pTransaction->SaveContract();
12757 
12758  // set up the ledger
12759  OTLedger theLedger(USER_ID, ASSET_ACCT_ID, SERVER_ID);
12760  theLedger.GenerateLedger(
12761  ASSET_ACCT_ID, SERVER_ID,
12762  OTLedger::message); // bGenerateLedger defaults to
12763  // false, which is correct.
12764  theLedger.AddTransaction(*pTransaction); // now the ledger
12765  // "owns" and will
12766  // handle cleaning
12767  // up the
12768  // transaction.
12769 
12770  // sign the ledger
12771  theLedger.SignContract(theNym);
12772  theLedger.SaveContract();
12773 
12774  // extract the ledger in ascii-armored form... encoding...
12775  OTString strLedger(theLedger);
12776  OTASCIIArmor ascLedger(strLedger);
12777 
12778  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
12779  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
12780  theMessage.m_strRequestNum.Format(
12781  "%lld", lRequestNumber); // Always have to send this.
12782  theNym.IncrementRequestNum(
12783  theNym, strServerID); // since I used it for a server
12784  // request, I have to increment it
12785 
12786  // (1) Set up member variables
12787  theMessage.m_strCommand = "notarizeTransactions";
12788  theMessage.m_strNymID = strNymID;
12789  theMessage.m_strServerID = strServerID;
12790  theMessage.SetAcknowledgments(
12791  theNym); // Must be called AFTER
12792  // theMessage.m_strServerID is already set. (It
12793  // uses it.)
12794 
12795  theMessage.m_strAcctID = str_ASSET_ACCT_ID;
12796  theMessage.m_ascPayload = ascLedger;
12797 
12798  OTIdentifier NYMBOX_HASH;
12799  const std::string str_server(strServerID.Get());
12800  const bool bNymboxHash =
12801  theNym.GetNymboxHash(str_server, NYMBOX_HASH);
12802 
12803  if (bNymboxHash)
12804  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
12805  else
12806  otErr
12807  << "Failed getting NymboxHash from Nym for server: "
12808  << str_server << "\n";
12809 
12810  // (2) Sign the Message
12811  theMessage.SignContract(theNym);
12812 
12813  // (3) Save the Message (with signatures and all, back to
12814  // its internal member m_strRawFile.)
12815  theMessage.SaveContract();
12816 
12817  bSendCommand = true;
12818  lReturnValue = lRequestNumber;
12819  }
12820 
12821  if (!bSendCommand) {
12822  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF
12823  // AVAILABLE NUMBERS.
12824  theNym.AddTransactionNum(theNym, strServerID,
12825  lStoredTransactionNumber,
12826  false); // bSave=true
12827  theNym.AddTransactionNum(theNym, strServerID,
12828  lClosingTransactionNoAssetAcct,
12829  false); // bSave=true
12830  theNym.AddTransactionNum(theNym, strServerID,
12831  lClosingTransactionNoCurrencyAcct,
12832  true); // bSave=true
12833  }
12834  } // Got Transaction Num
12835  }
12836  } // else if (OTClient::marketOffer == requestedCommand) // MARKET OFFER
12837  break;
12838  case OTClient::signContract: // Sign a CONTRACT. (Sends no message to
12839  // server.)
12840  {
12841  otOut << "Is the contract a server contract, or an asset contract "
12842  "[s/a]: ";
12843  OTString strContractType;
12844  strContractType.OTfgets(std::cin);
12845 
12846  char cContractType = 's';
12847  bool bIsAssetContract = strContractType.At(0, cContractType);
12848 
12849  if (bIsAssetContract) {
12850  if ('S' == cContractType || 's' == cContractType)
12851  bIsAssetContract = false;
12852  }
12853 
12854  otOut
12855  << "Is the contract properly escaped already? (If escaped, all "
12856  "lines beginning with ----- will instead appear as - ----- "
12857  ") "
12858  "[y/n]: ";
12859  // User input.
12860  // I need a from account, Yes even in a deposit, it's still the
12861  // "From"
12862  // account.
12863  // The "To" account is only used for a transfer. (And perhaps for a
12864  // 2-way trade.)
12865  OTString strEscape;
12866  strEscape.OTfgets(std::cin);
12867 
12868  char cEscape = 'n';
12869  bool bEscaped = strEscape.At(0, cEscape);
12870 
12871  if (bEscaped) {
12872  if ('N' == cEscape || 'n' == cEscape) bEscaped = false;
12873  }
12874 
12875  otOut << "Please enter an unsigned asset contract; terminate with "
12876  "~ on "
12877  "a new line:\n> ";
12878  OTString strContract;
12879  char decode_buffer[200]; // Safe since we only read
12880  // sizeof(decode_buffer)-1
12881 
12882  do {
12883  decode_buffer[0] = 0; // Make it fresh.
12884 
12885  if (!fgets(decode_buffer, sizeof(decode_buffer) - 1, stdin) &&
12886  (decode_buffer[0] != '~')) {
12887  if (!bEscaped && decode_buffer[0] == '-') {
12888  strContract.Concatenate("- ");
12889  }
12890  strContract.Concatenate("%s", decode_buffer);
12891  otOut << "> ";
12892  }
12893  else {
12894  break;
12895  }
12896 
12897  } while (decode_buffer[0] != '~');
12898 
12899  OTServerContract theServerContract;
12900  OTAssetContract theAssetContract;
12901 
12902  OTContract* pContract =
12903  bIsAssetContract
12904  ? dynamic_cast<OTContract*>(&theAssetContract)
12905  : dynamic_cast<OTContract*>(&theServerContract);
12906 
12907  pContract->CreateContract(strContract, theNym);
12908 
12909  // re-using strContract here for output this time.
12910  strContract.Release();
12911  pContract->SaveContractRaw(strContract);
12912 
12913  OTString strNewID;
12914  pContract->GetIdentifier(strNewID);
12915 
12916  otOut << ".\n..\n...\n....\n.....\n......\n.......\n........\n....."
12917  "...."
12918  "\n\nNEW CONTRACT ID: " << strNewID << "\n\n";
12919 
12920  std::cout << strContract << std::endl;
12921 
12922  return 0;
12923  }
12924  break;
12925  case OTClient::writeCheque: // Write a CHEQUE. (Sends no message to server.)
12926  {
12927  OTString strFromAcct;
12928 
12929  if (nullptr == pAccount) {
12930  otOut << "Please enter an Asset Account ID (to draw the cheque "
12931  "from): ";
12932  // User input.
12933  // I need a from account
12934  strFromAcct.OTfgets(std::cin);
12935 
12936  if (strFromAcct.GetLength() < 2) return (-1);
12937 
12938  const OTIdentifier ACCOUNT_ID(strFromAcct);
12939 
12940  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
12941  pAccount->GetIdentifier(strFromAcct);
12942  CONTRACT_ID = pAccount->GetAssetTypeID();
12943  CONTRACT_ID.GetString(strContractID);
12944  }
12945  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
12946  strFromAcct.Get())) != nullptr) {
12947  pAccount->GetIdentifier(strFromAcct);
12948  CONTRACT_ID = pAccount->GetAssetTypeID();
12949  CONTRACT_ID.GetString(strContractID);
12950  }
12951  else {
12952  otErr << "Unable to write cheque without account to draw from. "
12953  "On comand line, try adding: --myacct ACCOUNT_ID\n";
12954  return (-1);
12955  }
12956  }
12957  else {
12958  pAccount->GetIdentifier(strFromAcct);
12959  CONTRACT_ID = pAccount->GetAssetTypeID();
12960  CONTRACT_ID.GetString(strContractID);
12961  }
12962 
12963  const OTIdentifier ACCOUNT_ID(strFromAcct);
12964 
12965  if (pAccount->GetPurportedServerID() != SERVER_ID) {
12966  otErr << "OTClient::ProcessUserCommand: "
12967  "pAccount->GetPurportedServerID() doesn't match "
12968  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
12969  return (-1);
12970  }
12971 
12972  OTString strRecipientNym;
12973 
12974  if (nullptr == pHisNymID) {
12975  otOut << "Enter Recipient's Nym ID (full ID -- no partials here.) "
12976  "Blank IS allowed: ";
12977 
12978  // User input.
12979  // I need a from account
12980  strRecipientNym.OTfgets(std::cin);
12981 
12982  // if (strRecipientNym.GetLength() < 2) // blank cheques
12983  // are allowed.
12984  // return (-1);
12985  }
12986  else {
12987  pHisNymID->GetString(strRecipientNym);
12988  }
12989 
12990  // Todo add partial lookups here from wallet and/or address book.
12991 
12992  const OTIdentifier MY_NYM_ID(theNym);
12993 
12994  const OTIdentifier HIS_NYM_ID(strRecipientNym);
12995 
12996  OTString strAmount;
12997  if (0 == lTransactionAmount) {
12998  otOut << "Please enter an amount: ";
12999  // User input.
13000  // I need an amount
13001  strAmount.OTfgets(std::cin);
13002  }
13003 
13004  const int64_t lTotalAmount =
13005  (0 == lTransactionAmount)
13006  ? // If nothing was passed in, then use atol(strAmount),
13007  (atol(strAmount.Exists() ? strAmount.Get() : "0"))
13008  : lTransactionAmount; // otherwise lTransactionAmount.
13009 
13010  // To write a cheque, we need to burn one of our transaction numbers.
13011  // (Presumably the wallet
13012  // is also storing a couple of these, since they are needed to perform
13013  // any transaction.)
13014  //
13015  // I don't have to contact the server to write a cheque -- as long as I
13016  // already have a transaction
13017  // number I can use to write it. Otherwise I'd have to ask the server to
13018  // send me one first.
13019 
13020  int64_t lTransactionNumber = 0;
13021 
13022  if (false ==
13023  theNym.GetNextTransactionNum(theNym, strServerID,
13024  lTransactionNumber)) {
13025  otOut << "Cheques are written offline, but you still need a "
13026  "transaction number\n(and you have none, currently.) Try "
13027  "using 'n' to request another transaction number.\n";
13028  return (-1);
13029  }
13030 
13031  OTCheque theCheque(pAccount->GetRealServerID(),
13032  pAccount->GetAssetTypeID());
13033 
13034  // Memo
13035  otOut << "Enter a memo for your check: ";
13036  OTString strChequeMemo;
13037  strChequeMemo.OTfgets(std::cin);
13038 
13039  // Valid date range (in seconds)
13040  otOut << " 6 minutes == 360 Seconds\n10 minutes == 600 "
13041  "Seconds\n1 hour == 3600 Seconds\n1 day == "
13042  " 86400 Seconds\n30 days == 2592000 Seconds\n3 months "
13043  "== 7776000 Seconds\n6 months == 15552000 Seconds\n\n";
13044 
13045  int64_t lExpirationInSeconds =
13047  otOut << "How many seconds before cheque expires? (defaults to 1 hour: "
13048  << lExpirationInSeconds << "): ";
13049  OTString strTemp;
13050  strTemp.OTfgets(std::cin);
13051 
13052  if (strTemp.GetLength() > 1) lExpirationInSeconds = atol(strTemp.Get());
13053 
13054  time64_t VALID_FROM =
13055  OTTimeGetCurrentTime(); // This time is set to TODAY NOW
13056 
13057  otOut << "Cheque may be cashed STARTING date (defaults to now, in "
13058  "seconds) [" << VALID_FROM << "]: ";
13059  strTemp.Release();
13060  strTemp.OTfgets(std::cin);
13061 
13062  if (strTemp.GetLength() > 2)
13063  VALID_FROM = OTTimeGetTimeFromSeconds(strTemp.Get());
13064 
13065  const time64_t VALID_TO = OTTimeAddTimeInterval(
13066  VALID_FROM, lExpirationInSeconds); // now + 3600
13067 
13068  bool bIssueCheque = theCheque.IssueCheque(
13069  lTotalAmount, lTransactionNumber, VALID_FROM, VALID_TO, ACCOUNT_ID,
13070  MY_NYM_ID, strChequeMemo,
13071  (strRecipientNym.GetLength() > 2)
13072  ? &(HIS_NYM_ID)
13073  : nullptr); // blank cheques are allowed.
13074 
13075  if (bIssueCheque) {
13076  theCheque.SignContract(theNym);
13077  theCheque.SaveContract();
13078 
13079  OTString strCheque(theCheque);
13080 
13081  otOut << "\n\nOUTPUT (writeCheque):\n\n\n";
13082  // otOut actually goes to stderr, whereas the cout below is
13083  // actually sent to standard output.
13084  std::cout << strCheque << std::endl;
13085  }
13086  else {
13087  otOut << "Failed trying to issue the cheque!\n";
13088 
13089  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
13090  // NUMBERS.
13091  theNym.AddTransactionNum(theNym, strServerID, lTransactionNumber,
13092  true); // bSave=true
13093  }
13094 
13095  return -1;
13096  } break;
13097  case OTClient::proposePaymentPlan: // Propose a payment plan (sends no
13098  // message to server.)
13099  {
13100  OTString strMerchantAcct;
13101 
13102  if (nullptr == pAccount) {
13103  otOut
13104  << "You are the Merchant, proposing this payment plan so "
13105  "your customer can confirm it.\nAfter this command, use "
13106  "'confirm' (customer) to confirm it, and then activate "
13107  "it "
13108  "using\n'plan' (customer) from the OT prompt, or "
13109  "'--activateplan' from the command line.\n\nEnter the "
13110  "Merchant's (your) Asset Account ID that the payments "
13111  "will go to: ";
13112  // User input.
13113  // I need a from account
13114  strMerchantAcct.OTfgets(std::cin);
13115 
13116  if (strMerchantAcct.GetLength() < 2) return -1;
13117 
13118  const OTIdentifier ACCOUNT_ID(strMerchantAcct);
13119 
13120  if ((pAccount = m_pWallet->GetAccount(ACCOUNT_ID)) != nullptr) {
13121  pAccount->GetIdentifier(strMerchantAcct);
13122  CONTRACT_ID = pAccount->GetAssetTypeID();
13123  CONTRACT_ID.GetString(strContractID);
13124  }
13125  else if ((pAccount = m_pWallet->GetAccountPartialMatch(
13126  strMerchantAcct.Get())) != nullptr) {
13127  pAccount->GetIdentifier(strMerchantAcct);
13128  CONTRACT_ID = pAccount->GetAssetTypeID();
13129  CONTRACT_ID.GetString(strContractID);
13130  }
13131  else {
13132  otErr
13133  << "Unable to propose payment plan without account to "
13134  "pay to. Try adding: --myacct ACCOUNT_ID\n";
13135  return -1;
13136  }
13137  }
13138  else {
13139  pAccount->GetIdentifier(strMerchantAcct);
13140  CONTRACT_ID = pAccount->GetAssetTypeID();
13141  CONTRACT_ID.GetString(strContractID);
13142  }
13143 
13144  const OTIdentifier MY_ACCT_ID(strMerchantAcct);
13145 
13146  if (pAccount->GetPurportedServerID() != SERVER_ID) {
13147  otErr << "OTClient::ProcessUserCommand: "
13148  "pAccount->GetPurportedServerID() doesn't match "
13149  "SERVER_ID.\n(Try adding: --server SERVER_ID)\n";
13150  return -1;
13151  }
13152 
13153  // pAccount is the MERCHANT's (recipient) account. CONTRACT_ID is
13154  // its
13155  // ASSET_TYPE.
13156  // strContractID is the string version of that. And strMerchantAcct
13157  // is
13158  // the string
13159  // version of pAccount's ID.
13160  // theNym, FYI, is the Merchant's Nym.
13161 
13162  OTString strCustomerAcct;
13163 
13164  if (nullptr == pHisAcctID) {
13165  otOut << "Enter Customer's Asset Account ID that payments will "
13166  "come FROM (no partials): ";
13167 
13168  // User input.
13169  // I need a from account
13170  strCustomerAcct.OTfgets(std::cin);
13171 
13172  if (strCustomerAcct.GetLength() < 2) return -1;
13173  }
13174  else {
13175  pHisAcctID->GetString(strCustomerAcct);
13176  }
13177 
13178  const OTIdentifier HIS_ACCT_ID(strCustomerAcct);
13179 
13180  // HIS_ACCT_ID is the Customer's asset account id.
13181  // strCustomerAcct is the OTString version of that.
13182 
13183  OTString strCustomerNym;
13184 
13185  if (nullptr == pHisNymID) {
13186  otOut << "Enter Customer's Nym ID (full ID -- no partials "
13187  "here): ";
13188 
13189  // User input.
13190  // I need a from account
13191  strCustomerNym.OTfgets(std::cin);
13192 
13193  if (strCustomerNym.GetLength() < 2) return -1;
13194  }
13195  else {
13196  pHisNymID->GetString(strCustomerNym);
13197  }
13198 
13199  // Todo add partial lookups here from wallet and/or address book.
13200 
13201  const OTIdentifier MY_NYM_ID(theNym);
13202 
13203  const OTIdentifier HIS_NYM_ID(strCustomerNym);
13204 
13205  // HIS_NYM_ID is the Customer's nym id. MY_NYM_ID is the Merchant's.
13206  // strCustomerNym is the OTString version of HIS_NYM_ID.
13207 
13208  OTString strConsideration, strTemp;
13209 
13210  otOut << "Enter a memo describing consideration for the payment "
13211  "plan: ";
13212  strConsideration.OTfgets(std::cin);
13213 
13214  // To write a payment plan, like a cheque, we need to burn one of
13215  // our
13216  // transaction numbers. (Presumably
13217  // the wallet is also storing a couple of these, since they are
13218  // needed
13219  // to perform any transaction.)
13220  //
13221  // I don't have to contact the server to write a payment plan -- as
13222  // long
13223  // as I already have a transaction
13224  // number I can use to write it. Otherwise I'd have to ask the
13225  // server to
13226  // send me one first.
13227 
13228  if (theNym.GetTransactionNumCount(strServerID) < 2) {
13229  otOut
13230  << "Payment Plans are written offline, but you still need "
13231  "a "
13232  "2 transaction numbers\n(and you don't, currently.) Try "
13233  "using 'n' to request another transaction number.\n";
13234  return -1;
13235  }
13236 
13237  /*
13238  OTPaymentPlan(const OTIdentifier& SERVER_ID, const
13239  OTIdentifier& ASSET_ID, const OTIdentifier& SENDER_ACCT_ID, const
13240  OTIdentifier&
13241  SENDER_USER_ID, const OTIdentifier& RECIPIENT_ACCT_ID, const
13242  OTIdentifier&
13243  RECIPIENT_USER_ID);
13244 
13245  */
13246  OTPaymentPlan thePlan(pAccount->GetRealServerID(),
13247  pAccount->GetAssetTypeID(), HIS_ACCT_ID,
13248  HIS_NYM_ID, MY_ACCT_ID, MY_NYM_ID);
13249 
13250  // Valid date range (in seconds)
13251  otOut
13252  << " 6 minutes == 360 Seconds\n10 minutes == "
13253  "600 "
13254  "Seconds\n1 hour == 3600 Seconds\n1 day "
13255  "== "
13256  " 86400 Seconds\n30 days == 2592000 Seconds\n3 "
13257  "months == 7776000 Seconds\n6 months == "
13258  "15552000 Seconds\n\n";
13259 
13260  int64_t lExpirationInSeconds =
13262  otOut << "How many seconds before payment plan expires? (defaults "
13263  "to 1 "
13264  "day: " << lExpirationInSeconds << "): ";
13265  strTemp.Release();
13266  strTemp.OTfgets(std::cin);
13267 
13268  if (strTemp.GetLength() > 1)
13269  lExpirationInSeconds = atol(strTemp.Get());
13270 
13271  time64_t VALID_FROM =
13272  OTTimeGetCurrentTime(); // This time is set to TODAY NOW
13273 
13274  otOut << "Payment plan becomes valid for processing STARTING "
13275  "date\n(defaults to now, in seconds) [" << VALID_FROM
13276  << "]: ";
13277  strTemp.Release();
13278  strTemp.OTfgets(std::cin);
13279 
13280  if (strTemp.GetLength() > 2)
13281  VALID_FROM = OTTimeGetTimeFromSeconds(strTemp.Get());
13282 
13283  const time64_t VALID_TO = OTTimeAddTimeInterval(
13284  VALID_FROM, lExpirationInSeconds); // now + 86400
13285 
13286  // This pulls two transaction numbers off of pMerchantNym.
13287  // (So we have to put them back if there is some early failure and
13288  // crap-out...)
13289  //
13290  bool bSuccessSetAgreement = thePlan.SetProposal(
13291  theNym, strConsideration, VALID_FROM, VALID_TO);
13292 
13293  if (!bSuccessSetAgreement) {
13294  otOut << "Failed trying to set the proposal!\n";
13295 
13296  return -1;
13297  }
13298 
13299  bool bSuccessSetInitialPayment = true; // the default, in case user
13300  // chooses not to even have this
13301  // payment.
13302  bool bSuccessSetPaymentPlan =
13303  true; // the default, in case user chooses
13304  // not to have a payment plan
13305 
13306  otOut << "What is the Initial Payment Amount, if any? [0]: ";
13307  strTemp.Release();
13308  strTemp.OTfgets(std::cin);
13309  int64_t lInitialPayment = atol(strTemp.Get());
13310 
13311  if (lInitialPayment > 0) {
13312  time64_t PAYMENT_DELAY =
13313  OT_TIME_MINUTE_IN_SECONDS; // 60 seconds.
13314 
13315  otOut << "From the Start Date forward, how int64_t until the "
13316  "Initial Payment should charge?\n(defaults to one "
13317  "minute, "
13318  "in seconds) [" << PAYMENT_DELAY << "]: ";
13319  strTemp.Release();
13320  strTemp.OTfgets(std::cin);
13321 
13322  if ((strTemp.GetLength() > 1) && atol(strTemp.Get()) > 0)
13323  PAYMENT_DELAY = OTTimeGetTimeFromSeconds(strTemp.Get());
13324 
13325  bSuccessSetInitialPayment =
13326  thePlan.SetInitialPayment(lInitialPayment, PAYMENT_DELAY);
13327  }
13328 
13329  if (!bSuccessSetInitialPayment) {
13330  otOut << "Failed trying to set the initial payment!\n";
13331 
13332  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
13333  // NUMBERS.
13334  thePlan.HarvestClosingNumbers(
13335  theNym); // puts the relevant numbers back onto Nym.
13336 
13337  return -1;
13338  }
13339 
13340  otOut << "What is the regular payment amount, if any? [0]: ";
13341  strTemp.Release();
13342  strTemp.OTfgets(std::cin);
13343  int64_t lRegularPayment = atol(strTemp.Get());
13344 
13345  if (lRegularPayment > 0) // If there are regular payments.
13346  {
13347 
13348  time64_t PAYMENT_DELAY =
13349  OTTimeGetTimeFromSeconds(120); // 120 seconds.
13350 
13351  otOut
13352  << "From the Start Date forward, how int64_t until the "
13353  "Regular Payments start?\n(defaults to two minutes, in "
13354  "seconds) [" << PAYMENT_DELAY << "]: ";
13355  strTemp.Release();
13356  strTemp.OTfgets(std::cin);
13357 
13358  if ((strTemp.GetLength() > 1) && atol(strTemp.Get()) > 0)
13359  PAYMENT_DELAY = OTTimeGetTimeFromSeconds(strTemp.Get());
13360 
13361  time64_t PAYMENT_PERIOD =
13362  OTTimeGetTimeFromSeconds(30); // 30 seconds.
13363 
13364  otOut << "Once payments begin, how much time should elapse "
13365  "between "
13366  "each payment?\n(defaults to thirty seconds) ["
13367  << PAYMENT_PERIOD << "]: ";
13368  strTemp.Release();
13369  strTemp.OTfgets(std::cin);
13370 
13371  if ((strTemp.GetLength() > 1) && atol(strTemp.Get()) > 0)
13372  PAYMENT_PERIOD = OTTimeGetTimeFromSeconds(strTemp.Get());
13373 
13374  time64_t PLAN_LENGTH =
13375  OT_TIME_ZERO; // 0 seconds (for no max length).
13376 
13377  otOut << "From start date, do you want the plan to expire "
13378  "after a "
13379  "certain maximum time?\n(defaults to 0 for no) ["
13380  << PLAN_LENGTH << "]: ";
13381  strTemp.Release();
13382  strTemp.OTfgets(std::cin);
13383 
13384  if (strTemp.GetLength() > 1)
13385  PLAN_LENGTH = OTTimeGetTimeFromSeconds(strTemp.Get());
13386 
13387  otOut
13388  << "Should there be some maximum number of payments? (Zero "
13389  "for no maximum.) [0]: ";
13390  strTemp.Release();
13391  strTemp.OTfgets(std::cin);
13392  int32_t nMaxPayments = atoi(strTemp.Get());
13393 
13394  bSuccessSetPaymentPlan = thePlan.SetPaymentPlan(
13395  lRegularPayment, PAYMENT_DELAY, PAYMENT_PERIOD, PLAN_LENGTH,
13396  nMaxPayments);
13397  }
13398 
13399  if (!bSuccessSetPaymentPlan) {
13400  otOut << "Failed trying to set the payment plan!\n";
13401 
13402  // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE
13403  // NUMBERS.
13404  thePlan.HarvestClosingNumbers(
13405  theNym); // puts the relevant numbers back onto Nym.
13406 
13407  return -1;
13408  }
13409 
13410  thePlan.SignContract(theNym);
13411  thePlan.SaveContract();
13412 
13413  OTString strPlan(thePlan);
13414 
13415  otOut
13416  << "\n\n(Make sure to have your Customer 'confirm' the payment "
13417  "plan, before he activates it at the server using the "
13418  "'plan' "
13419  "command in the OT prompt, or --activateplan at the "
13420  "command-line):\n\n";
13421 
13422  std::cout << strPlan << std::endl;
13423 
13424  return 0; // sends no server message in this case.
13425  }
13426  break;
13427  case OTClient::confirmPaymentPlan: // Confirm a payment plan (sends no
13428  // message to server.)
13429  {
13430  otOut
13431  << "(You are the customer, confirming a payment plan that the "
13432  "merchant has just sent you.)\n\n";
13433 
13434  OTPaymentPlan thePlan;
13435 
13436  otOut
13437  << "Please enter plaintext payment plan. Terminate with ~ on a "
13438  "new line:\n> ";
13439  OTString strPlan;
13440  char decode_buffer[200]; // Safe since we only read
13441  // sizeof(decode_buffer)-1
13442 
13443  do {
13444  decode_buffer[0] = 0; // Make it fresh.
13445 
13446  if (!fgets(decode_buffer, sizeof(decode_buffer) - 1, stdin) &&
13447  (decode_buffer[0] != '~')) {
13448  strPlan.Concatenate("%s", decode_buffer);
13449  otOut << "> ";
13450  }
13451  else {
13452  break;
13453  }
13454 
13455  } while (decode_buffer[0] != '~');
13456 
13457  if (!thePlan.LoadContractFromString(strPlan)) {
13458  otOut << "Unable to load payment plan from string.\n";
13459  return -1;
13460  }
13461 
13462  OTPseudonym* pCustomerNym =
13463  m_pWallet->GetNymByID(thePlan.GetSenderUserID());
13464 
13465  if (nullptr == pCustomerNym) {
13466  otOut << "The customer Nym on this payment plan (you, "
13467  "supposedly) "
13468  "wasn't found in the wallet. Try 'load'.\n";
13469  return -1;
13470  }
13471  OTPseudonym* pMerchantNym =
13472  m_pWallet->GetNymByID(thePlan.GetRecipientUserID());
13473 
13474  // if (nullptr == pMerchantNym)
13475  // {
13476  // otOut << "Merchant Nym wasn't found in the wallet. Try
13477  // 'load'.\n";
13478  // // TODO add lookups from address book here as well?
13479  // return -1;
13480  // }
13481  if (false ==
13482  thePlan.Confirm(*pCustomerNym, pMerchantNym,
13483  &thePlan.GetRecipientUserID())) {
13484  otOut << "Error while confirming payment plan. Sorry.\n";
13485  return -1;
13486  }
13487 
13488  thePlan.SignContract(*pCustomerNym);
13489  thePlan.SaveContract();
13490 
13491  OTString strOutput(thePlan);
13492 
13493  otOut
13494  << "\n\nMake sure to submit the payment plan to the server, to "
13495  "activate it:\n\n";
13496 
13497  // The above otOut actually outputs to stderr (cerr). That's
13498  // on purpose.
13499  // But the below output actually outputs to stdout, which is also on
13500  // purpose.
13501  //
13502  std::cout << strOutput << std::endl;
13503 
13504  return 0; // no server message being sent, in this case.
13505  }
13506  break;
13507  case OTClient::paymentPlan: // Activate a PAYMENT PLAN
13508  {
13509  const OTIdentifier USER_ID(theNym);
13510 
13511  OTPaymentPlan thePlan;
13512 
13513  otOut << "Please enter plaintext payment plan. Terminate with ~ on a "
13514  "new line:\n> ";
13515  OTString strPlan;
13516  char decode_buffer[200]; // Safe since we only read
13517  // sizeof(decode_buffer)-1
13518 
13519  do {
13520  decode_buffer[0] = 0; // Make it fresh.
13521 
13522  if (!fgets(decode_buffer, sizeof(decode_buffer) - 1, stdin) &&
13523  (decode_buffer[0] != '~')) {
13524  strPlan.Concatenate("%s", decode_buffer);
13525  otOut << "> ";
13526  }
13527  else {
13528  break;
13529  }
13530 
13531  } while (decode_buffer[0] != '~');
13532 
13533  if (thePlan.LoadContractFromString(strPlan)) {
13534  const OTIdentifier ACCOUNT_ID(thePlan.GetSenderAcctID());
13535 
13536  OTAccount* pSenderAccount = m_pWallet->GetAccount(ACCOUNT_ID);
13537 
13538  if (nullptr == pSenderAccount) {
13539  otOut << "There is no account loaded on this wallet with that "
13540  "sender acct ID, sorry.\n";
13541  }
13542  if ((nullptr != pAccount) && (pSenderAccount != pAccount)) {
13543  otOut << "This Payment Plan is already confirmed, yet now you "
13544  "try to activate it, and you \nhave supplied a "
13545  "different account ID than the one that originally "
13546  "confirmed it.\nPerhaps it's just an unfortunate "
13547  "default in your ~/.ot/command-line-ot.opt file.\nBe "
13548  "explicit, and use: --myacct <acct_id>\n";
13549  }
13550  else {
13551  OTString strFromAcct(ACCOUNT_ID);
13552 
13553  // Create a transaction
13554  OTTransaction* pTransaction =
13556  USER_ID, ACCOUNT_ID, SERVER_ID,
13558  thePlan.GetTransactionNum());
13559 
13560  // set up the transaction item (each transaction may have
13561  // multiple items...)
13563  *pTransaction, OTItem::paymentPlan);
13564 
13565  strPlan.Release();
13566  thePlan.SaveContractRaw(strPlan);
13567 
13568  // Add the payment plan string as the attachment on the
13569  // transaction item.
13570  pItem->SetAttachment(strPlan); // The payment plan is contained
13571  // in the reference string.
13572 
13573  // sign the item
13574  pItem->SignContract(theNym);
13575  pItem->SaveContract();
13576 
13577  // the Transaction "owns" the item now and will handle cleaning
13578  // it up.
13579  pTransaction->AddItem(*pItem); // the Transaction's destructor
13580  // will cleanup the item. It
13581  // "owns" it now.
13582 
13583  // TRANSACTION AGREEMENT
13584 
13585  // pBalanceItem is signed and saved within this call. No need to
13586  // do that again.
13587  OTItem* pStatementItem =
13588  theNym.GenerateTransactionStatement(*pTransaction);
13589 
13590  if (nullptr !=
13591  pStatementItem) // will never be nullptr. Will assert
13592  // above before it gets here.
13593  pTransaction->AddItem(*pStatementItem); // Better not be
13594  // nullptr... message
13595  // will fail... But
13596  // better check
13597  // anyway.
13598 
13599  // sign the transaction
13600  pTransaction->SignContract(theNym);
13601  pTransaction->SaveContract();
13602 
13603  // set up the ledger
13604  OTLedger theLedger(USER_ID, ACCOUNT_ID, SERVER_ID);
13605  theLedger.GenerateLedger(ACCOUNT_ID, SERVER_ID,
13606  OTLedger::message); // bGenerateLedger
13607  // defaults to
13608  // false, which is
13609  // correct.
13610  theLedger.AddTransaction(*pTransaction); // now the ledger
13611  // "owns" and will
13612  // handle cleaning up
13613  // the transaction.
13614 
13615  // sign the ledger
13616  theLedger.SignContract(theNym);
13617  theLedger.SaveContract();
13618 
13619  // extract the ledger in ascii-armored form... encoding...
13620  OTString strLedger(theLedger);
13621  OTASCIIArmor ascLedger(strLedger);
13622 
13623  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
13624  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
13625  theMessage.m_strRequestNum.Format(
13626  "%lld", lRequestNumber); // Always have to send this.
13627  theNym.IncrementRequestNum(
13628  theNym, strServerID); // since I used it for a server
13629  // request, I have to increment it
13630 
13631  // (1) Set up member variables
13632  theMessage.m_strCommand = "notarizeTransactions";
13633  theMessage.m_strNymID = strNymID;
13634  theMessage.m_strServerID = strServerID;
13635  theMessage.SetAcknowledgments(
13636  theNym); // Must be called AFTER theMessage.m_strServerID is
13637  // already set. (It uses it.)
13638 
13639  theMessage.m_strAcctID = strFromAcct;
13640  theMessage.m_ascPayload = ascLedger;
13641 
13642  OTIdentifier NYMBOX_HASH;
13643  const std::string str_server(strServerID.Get());
13644  const bool bNymboxHash =
13645  theNym.GetNymboxHash(str_server, NYMBOX_HASH);
13646 
13647  if (bNymboxHash)
13648  NYMBOX_HASH.GetString(theMessage.m_strNymboxHash);
13649  else
13650  otErr << "Failed getting NymboxHash from Nym for server: "
13651  << str_server << "\n";
13652 
13653  // (2) Sign the Message
13654  theMessage.SignContract(theNym);
13655 
13656  // (3) Save the Message (with signatures and all, back to its
13657  // internal member m_strRawFile.)
13658  theMessage.SaveContract();
13659 
13660  lReturnValue = lRequestNumber;
13661 
13662  } // pAccount not nullptr
13663  } // thePlan.LoadContractFromString()
13664  else {
13665  otOut << "Unable to load payment plan from string. Sorry.\n";
13666  }
13667 
13668  } // else if (OTClient::paymentPlan == requestedCommand) // PAYMENT PLAN
13669 
13670  /*
13671  else if (OTClient::withdrawTest == requestedCommand) // TEST OF TOKEN
13672  BLINDING. NOT PART OF THE REAL PROTOCOL.
13673  {
13674  // (0) Set up the REQUEST NUMBER and then INCREMENT IT
13675  theNym.GetCurrentRequestNum(strServerID, lRequestNumber);
13676  theMessage.m_strRequestNum.Format("%lld", lRequestNumber); // Always have to
13677  send this.
13678  theNym.IncrementRequestNum(strServerID); // since I used it for a server
13679  request, I have to increment it
13680 
13681  // (1) Set up member variables
13682  theMessage.m_strCommand = "debitAccount";
13683  theMessage.m_strNymID = strNymID;
13684  theMessage.m_strServerID = strServerID;
13685  theMessage.SetAcknowledgments(theNym); // Must be called AFTER
13686  theMessage.m_strServerID is already set. (It uses it.)
13687 
13688  theMessage.m_strAssetID = strContractID;// the hash of the
13689  contract is the AssetID
13690 
13691  // (2) Sign the Message
13692  OTContract& aSigningDoc = theMessage;
13693  aSigningDoc.SignContract(theNym);
13694 
13695  // (3) Save the Message (with signatures and all, back to its internal
13696  member m_strRawFile.)
13697  theMessage.SaveContract();
13698 
13699  bSendCommand = true;
13700  }
13701  */
13702  break;
13703 
13704  default: {
13705  otOut << "\n";
13706  }
13707  } // Get out og the big switch statement!
13708 
13709  return CalcReturnVal(lReturnValue);
13710 }
13711 
13723  const OTPseudonym& theNym, const OTString& strCA_FILE,
13724  const OTString& strKEY_FILE, const OTString& strKEY_PASSWORD) const
13725 {
13726  OTIdentifier SERVER_ID;
13727  OTString SERVER_NAME;
13728 
13729  if (m_pWallet && m_pWallet->GetServer(0, SERVER_ID, SERVER_NAME)) {
13730  OTServerContract* pServer = m_pWallet->GetServerContract(SERVER_ID);
13731 
13732  if (nullptr != pServer) {
13733  OT_ASSERT(nullptr != m_pConnection);
13734  return m_pConnection->Connect(theNym, *pServer, strCA_FILE,
13735  strKEY_FILE, strKEY_PASSWORD);
13736  }
13737  }
13738 
13739  return false;
13740 }
13741 
13748  OTPseudonym& theNym,
13749  TransportCallback* pCallback) const
13750 {
13751  OT_ASSERT(nullptr != pCallback);
13752  OT_ASSERT(nullptr != m_pConnection);
13753 
13754  return m_pConnection->SetFocus(theNym, theServerContract, pCallback);
13755 }
13756 
13759 {
13760  if (m_bInitialized) {
13761  otWarn
13762  << "OTClient::InitClient: Already initialized. (Returning true.)\n";
13763  return false;
13764  }
13765  m_bInitialized = true;
13766 
13767  m_pConnection = new OTServerConnection(theWallet, *this);
13768  m_pWallet = &theWallet;
13769 
13770  return true;
13771 }
13772 
13774  : m_pWallet(nullptr)
13775  , m_bRunningAsScript(false)
13776  , m_lMostRecentRequestNumber(0)
13777  , m_pConnection(nullptr)
13778  , m_bInitialized(false)
13779 {
13780 }
13781 
13783 {
13784  if (nullptr != m_pConnection) delete m_pConnection;
13785  m_pConnection = nullptr;
13786 }
13787 
13788 } // namespace opentxs
EXPORT bool LoadInboxFromString(const OTString &strBox)
Definition: OTLedger.cpp:487
EXPORT int32_t GetIssuedNumCount(const OTIdentifier &theServerID) const
EXPORT bool StorePlainString(std::string strContents, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:698
EXPORT Storage * GetDefaultStorage()
Definition: OTStorage.cpp:480
EXPORT void GetReferenceString(OTString &theStr) const
int64_t m_lNewRequestNum
Definition: OTMessage.hpp:230
OTASCIIArmor m_ascPayload2
Definition: OTMessage.hpp:218
EXPORT bool RemoveTransaction(int64_t lTransactionNum, bool bDeleteIt=true)
Definition: OTLedger.cpp:1168
OTLOG_IMPORT OTLogStream otLog4
EXPORT void GetPublicCredentials(OTString &strCredList, OTString::Map *pmapCredFiles=nullptr) const
std::map< std::string, std::string > Map
Definition: OTString.hpp:162
void SetAmount(int64_t lAmount)
Definition: OTItem.hpp:479
EXPORT OTItem * GenerateBalanceStatement(int64_t lAdjustment, const OTTransaction &theOwner, OTPseudonym &theNym, const OTAccount &theAccount, OTLedger &theOutbox)
Definition: OTLedger.cpp:1532
EXPORT bool SetNymboxHash(const std::string &server_id, const OTIdentifier &theInput)
EXPORT bool SetFocusToServerAndNym(OTServerContract &theServerContract, OTPseudonym &theNym, TransportCallback *pCallback) const
Definition: OTClient.cpp:13747
static EXPORT OTAsymmetricKey * KeyFactory()
EXPORT Storable * CreateObject(StoredObjectType eType)
Definition: OTStorage.cpp:530
static EXPORT int64_t StringToLong(const std::string &number)
Definition: OTString.cpp:677
int32_t GetTransactionCount() const
Definition: OTLedger.hpp:332
EXPORT OTMessage * GetSentMessage(const int64_t &requestNum, const OTString &serverId, const OTString &nymId)
void ProcessDepositResponse(OTTransaction &theTransaction, const OTServerConnection &theConnection, const OTMessage &theReply) const
Definition: OTClient.cpp:3287
EXPORT bool Unpack(PackedBuffer &inBuf, Storable &outObj)
Definition: OTStorage.cpp:972
EXPORT bool SetPublicKey(const OTString &strKey, bool bEscaped=true)
bool SetFocus(OTPseudonym &theNym, OTServerContract &theServerContract, TransportCallback *pCallback)
static EXPORT OTTransaction * GenerateTransaction(const OTIdentifier &theUserID, const OTIdentifier &theAccountID, const OTIdentifier &theServerID, transactionType theType, int64_t lTransactionNum=0)
static EXPORT OTItem * CreateItemFromString(const OTString &strItem, const OTIdentifier &theServerID, int64_t lTransactionNumber)
Definition: OTItem.cpp:1473
int32_t CalcReturnVal(const int64_t &lRequestNumber)
Definition: OTClient.cpp:173
virtual EXPORT bool SignContract(const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr)
Definition: OTMessage.cpp:3873
EXPORT void SetNumberOfOrigin(int64_t lTransactionNum)
EXPORT void HarvestTransactionNumbers(const OTIdentifier &theServerID, OTPseudonym &SIGNER_NYM, OTPseudonym &theOtherNym, bool bSave=true)
int32_t GetCompletedCount()
Definition: OTTrade.hpp:284
const OTIdentifier & GetSenderUserID() const
static EXPORT Mint * MintFactory()
Definition: Mint.cpp:154
EXPORT OTItem * GenerateTransactionStatement(const OTTransaction &theOwner)
OTString m_strAcctID
Definition: OTMessage.hpp:205
EXPORT bool LoadFromString(const OTString &strNym, OTString::Map *pMapCredentials=nullptr, OTString *pstrReason=nullptr, const OTPassword *pImportPassword=nullptr)
Token::tokenState GetState() const
Definition: Token.hpp:347
EXPORT bool SetInboxHash(const std::string &acct_id, const OTIdentifier &theInput)
virtual EXPORT bool VerifySignature(const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr) const
Definition: OTMessage.cpp:3898
EXPORT void GetIdentifier(OTIdentifier &theIdentifier) const
EXPORT OTAccount * GetAccountPartialMatch(std::string PARTIAL_ID)
Definition: OTWallet.cpp:613
static EXPORT const OTString & Pubcred()
Definition: OTFolders.cpp:343
EXPORT bool SaveOutbox(OTIdentifier *pOutboxHash=nullptr)
Definition: OTLedger.cpp:881
EXPORT bool RemoveSentMessage(const int64_t &requestNum, const OTString &serverId, const OTString &nymId)
EXPORT void GetAttachment(OTString &theStr) const
Definition: OTItem.cpp:1397
OTString m_strInboxHash
Definition: OTMessage.hpp:196
EXPORT bool MakeOffer(bool bBuyingOrSelling, const int64_t &lPriceLimit, const int64_t &lTotalAssetsOffer, const int64_t &lMinimumIncrement, const int64_t &lTransactionNum, const time64_t &VALID_FROM=OT_TIME_ZERO, const time64_t &VALID_TO=OT_TIME_ZERO)
Definition: OTOffer.cpp:434
EXPORT void SetAcknowledgments(OTPseudonym &theNym)
Definition: OTMessage.cpp:262
EXPORT bool LoadRecordBox()
Definition: OTLedger.cpp:507
std::string to_string(const T &t)
int64_t m_lTransactionNum
Definition: OTMessage.hpp:239
virtual EXPORT bool VerifyAccount(const OTPseudonym &theNym)
Definition: OTLedger.cpp:187
#define OT_TIME_HOUR_IN_SECONDS
Definition: Common.hpp:177
EXPORT const OTIdentifier & GetAssetTypeID() const
Definition: OTAccount.cpp:449
int64_t GetTransactionNum() const
static EXPORT const OTString & PaymentInbox()
Definition: OTFolders.cpp:339
EXPORT bool Push(OTNym_or_SymmetricKey theOwner, const Token &theToken)
Definition: Purse.cpp:1575
EXPORT OTAssetContract * GetAssetContract(const OTIdentifier &theContractID)
Definition: OTWallet.cpp:1203
static EXPORT OTTransactionType * TransactionFactory(OTString strInput)
const int64_t & GetAmount() const
Definition: OTCheque.hpp:172
void ProcessPayDividendResponse(OTTransaction &theTransaction, const OTServerConnection &theConnection, const OTMessage &theReply) const
Definition: OTClient.cpp:3252
EXPORT bool SavePublicKey(const OTString &strPath) const
EXPORT bool DeleteBoxReceipt(OTLedger &theLedger)
EXPORT bool SaveContract()
EXPORT bool VerifyIssuedNum(const OTString &strServerID, const int64_t &lTransNum) const
time64_t OTTimeAddTimeInterval(time64_t lhs, int64_t rhs)
Definition: Common.hpp:238
EXPORT bool AddBlankNumbersToItem(const OTNumList &theAddition)
Definition: OTItem.cpp:1125
EXPORT void AddOutmail(OTMessage &theMessage)
int64_t GetMinimumTransfer() const
Definition: Basket.hpp:221
static EXPORT const char * PathSeparator()
Definition: OTLog.cpp:408
EXPORT OTLedger * LoadOutbox(OTPseudonym &nym) const
Definition: OTAccount.cpp:242
virtual bool VerifyAccount(const OTPseudonym &theNym)
EXPORT bool GetNextTransactionNum(OTPseudonym &SIGNER_NYM, const OTString &strServerID, int64_t &lTransNum, bool bSave=true)
const OTIdentifier & GetAssetID() const
EXPORT bool LoadInbox()
Definition: OTLedger.cpp:463
EXPORT bool GetAllTransactionNumbers(OTNumList &numlistOutput) const
Definition: OTPayment.cpp:533
EXPORT bool VerifyTentativeNum(const OTString &strServerID, const int64_t &lTransNum) const
static EXPORT const OTString & Nym()
Definition: OTFolders.cpp:327
EXPORT OTItem * GetItemInRefTo(int64_t lReference)
Purse * GetPendingWithdrawal() const
Definition: OTWallet.hpp:249
EXPORT bool SaveRecordBox()
Definition: OTLedger.cpp:924
EXPORT void AddPendingWithdrawal(const Purse &thePurse)
Definition: OTWallet.cpp:232
virtual EXPORT bool CreateContract(const OTString &strContract, const OTPseudonym &theSigner)
EXPORT void OTfgets(std::istream &ofs)
Definition: OTString.cpp:1159
EXPORT const OTAsymmetricKey & GetPublicEncrKey() const
EXPORT bool SetPayment(const OTString &strPayment)
Definition: OTPayment.cpp:1456
EXPORT int64_t GetTransactionNum(const OTIdentifier &theServerID, int32_t nIndex) const
EXPORT bool AddTransaction(OTTransaction &theTransaction)
Definition: OTLedger.cpp:1194
OTLOG_IMPORT OTLogStream otOut
EXPORT void AddAssetContract(const OTAssetContract &theContract)
Definition: OTWallet.cpp:752
EXPORT void GetNumList(OTNumList &theOutput)
bool ProcessServerReply(OTMessage &theReply, OTLedger *pNymbox=nullptr)
Definition: OTClient.cpp:3764
EXPORT void GetNote(OTString &theStr) const
Definition: OTItem.cpp:1430
OTString m_strAssetID
Definition: OTMessage.hpp:203
void SetNymName(const OTString &strName)
void load_str_trans_add_to_ledger(const OTIdentifier &the_nym_id, const OTString &str_trans, OTString str_box_type, const int64_t &lTransNum, OTPseudonym &the_nym, OTLedger &ledger)
Definition: OTClient.cpp:1946
OTLOG_IMPORT OTLogStream otLog3
bool RemoveTentativeNum(OTPseudonym &SIGNER_NYM, const OTString &strServerID, const int64_t &lTransNum, bool bSave)
EXPORT void SetAttachment(const OTString &theStr)
Definition: OTItem.cpp:1402
const OTIdentifier & GetAssetID() const
Definition: Purse.hpp:335
EXPORT bool SaveContractRaw(OTString &strOutput) const
EXPORT bool GetAccount(int32_t iIndex, OTIdentifier &THE_ID, OTString &THE_NAME)
Definition: OTWallet.cpp:431
EXPORT uint32_t GetLength() const
Definition: OTString.cpp:1040
OTString & GetNymName()
EXPORT int32_t ProcessUserCommand(OT_CLIENT_CMD_TYPE requestedCommand, OTMessage &theMessage, OTPseudonym &theNym, const OTServerContract &theServer, const OTAccount *pAccount=nullptr, int64_t lTransactionAmount=0, OTAssetContract *pMyAssetContract=nullptr, const OTIdentifier *pHisNymID=nullptr, const OTIdentifier *pHisAcctID=nullptr)
Definition: OTClient.cpp:8905
EXPORT bool AddTentativeNum(const OTString &strServerID, const int64_t &lTransNum)
EXPORT bool AddNewSubkey(const OTIdentifier &idMasterCredential, int32_t nBits=1024, const OTString::Map *pmapPrivate=nullptr, const OTPasswordData *pPWData=nullptr, OTString *pstrNewID=nullptr)
time64_t OTTimeGetTimeFromSeconds(int64_t seconds)
Definition: Common.hpp:215
EXPORT bool IssueTrade(OTOffer &offer, char stopSign=0, int64_t stopPrice=0)
Definition: OTTrade.cpp:1262
bool HasRecipient() const
Definition: OTCheque.hpp:180
EXPORT void Concatenate(const char *arg,...)
Definition: OTString.cpp:1334
EXPORT bool Seal(const OTPseudonym &theRecipient, const OTString &theInput)
Definition: OTEnvelope.cpp:521
int64_t time64_t
Definition: Common.hpp:209
static EXPORT const OTString & Inbox()
Definition: OTFolders.cpp:315
const int64_t & GetRequestNum() const
EXPORT bool WriteArmoredString(OTString &strOutput, const std::string str_type, bool bEscaped=false) const
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357
static EXPORT const OTString & Contract()
Definition: OTFolders.cpp:303
EXPORT int32_t GetTransactionNumCount(const OTIdentifier &theServerID) const
EXPORT void AddSentMessage(OTMessage &message)
bool ConnectToTheFirstServerOnList(const OTPseudonym &theNym, const OTString &strCA_FILE, const OTString &strKEY_FILE, const OTString &strKEY_PASSWORD) const
Definition: OTClient.cpp:13722
EXPORT bool LoadNymbox()
Definition: OTLedger.cpp:482
#define OT_TIME_SIX_MONTHS_IN_SECONDS
Definition: Common.hpp:170
EXPORT Storable * QueryObject(StoredObjectType theObjectType, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:788
EXPORT bool VerifyPseudonym() const
static EXPORT OTItem * CreateItemFromTransaction(const OTTransaction &theOwner, OTItem::itemType theType, const OTIdentifier *pDestinationAcctID=nullptr)
Definition: OTItem.cpp:1451
EXPORT bool SavePaymentInbox()
Definition: OTLedger.cpp:913
EXPORT void ReleaseSignatures()
Definition: OTContract.cpp:989
EXPORT bool Exists() const
Definition: OTString.cpp:1035
EXPORT bool SetString(const OTString &theData, bool bLineBreaks=true)
EXPORT std::string EncodeObject(Storable &theContents)
Definition: OTStorage.cpp:818
EXPORT void SetString(const char *szString)
EXPORT void SetIdentifier(const OTIdentifier &theIdentifier)
const char * GetTypeString() const
EXPORT void Format(const char *fmt,...)
Definition: OTString.cpp:1319
EXPORT void Push(OTMessage &message)
EXPORT void ReleaseTransactions()
Definition: OTLedger.cpp:2451
#define OT_TIME_DAY_IN_SECONDS
Definition: Common.hpp:176
OTMessageOutbuffer & GetMessageOutbuffer()
Definition: OTClient.hpp:363
EXPORT bool Connect(const OTPseudonym &, const OTServerContract &, const OTString &, const OTString &, const OTString &) const
EXPORT void SetReferenceString(const OTString &theStr)
EXPORT const OTPseudonym * GetContractPublicNym() const
Definition: OTContract.cpp:413
EXPORT bool Compare(const char *compare) const
Definition: OTString.cpp:1102
EXPORT bool GetHighestNum(const OTString &strServerID, int64_t &lHighestNum) const
static EXPORT Token * InstantiateAndGenerateTokenRequest(const Purse &thePurse, const OTPseudonym &theNym, Mint &theMint, int64_t lDenomination, int32_t nTokenCount=Token::GetMinimumPrototokenCount())
Definition: Token.cpp:1086
EXPORT OTItem * GetItem(OTItem::itemType theType)
EXPORT bool LoadOutboxFromString(const OTString &strBox)
Definition: OTLedger.cpp:492
OTNumList m_AcknowledgedReplies
Definition: OTMessage.hpp:225
void ProcessMessageOut(const char *buf, int32_t *pnExpectReply) const
Definition: OTClient.cpp:190
EXPORT bool RemoveTransactionNum(OTPseudonym &SIGNER_NYM, const OTString &strServerID, const int64_t &lTransNum)
const OTIdentifier & GetPurportedServerID() const
void SetIdentifier(const OTIdentifier &theID)
Definition: OTContract.hpp:268
const int64_t & GetFinishedSoFar() const
Definition: OTOffer.hpp:290
EXPORT void Set(const char *data, uint32_t enforcedMaxLength=0)
Definition: OTString.cpp:1055
time64_t OTTimeGetCurrentTime()
Definition: Common.hpp:211
EXPORT bool LoadLedgerFromString(const OTString &theStr)
Definition: OTLedger.cpp:1752
EXPORT bool AddTransactionNum(OTPseudonym &SIGNER_NYM, const OTString &strServerID, int64_t lTransNum, bool bSave)
bool ProcessInBuffer(const OTMessage &theServerReply) const
Definition: OTClient.cpp:247
EXPORT OTPseudonym * GetNymByID(const OTIdentifier &NYM_ID)
Definition: OTWallet.cpp:275
virtual EXPORT bool ProcessToken(const OTPseudonym &theNym, Mint &theMint, Token &theRequest)=0
EXPORT bool GetPublicKey(OTASCIIArmor &strKey) const
EXPORT bool Confirm(OTPseudonym &PAYER_NYM, OTPseudonym *pMERCHANT_NYM=nullptr, const OTIdentifier *p_id_MERCHANT_NYM=nullptr)
bool AcceptEntireNymbox(OTLedger &theNymbox, const OTIdentifier &theServerID, const OTServerContract &theServerContract, OTPseudonym &theNym, OTMessage &theMessage)
Definition: OTClient.cpp:259
OTServerContract * GetServerContract() const
EXPORT bool SaveExpiredBox()
Definition: OTLedger.cpp:935
EXPORT const OTAsymmetricKey & GetPublicAuthKey() const
EXPORT bool GetAsciiArmoredData(OTASCIIArmor &theArmoredText, bool bLineBreaks=true) const
Definition: OTEnvelope.cpp:162
int64_t GetAmount() const
Definition: OTItem.hpp:475
OTString m_strNymID2
Definition: OTMessage.hpp:200
void ProcessMessageOut(const char *buf, const int32_t *pnExpectReply)
EXPORT bool Output(std::set< int64_t > &theOutput) const
Definition: OTNumList.cpp:430
EXPORT bool GetCurrentRequestNum(const OTString &strServerID, int64_t &lReqNum) const
EXPORT int64_t GetClosingNum() const
EXPORT bool RemoveIssuedNum(OTPseudonym &SIGNER_NYM, const OTString &strServerID, const int64_t &lTransNum, bool bSave)
EXPORT bool RemoveAccount(const OTIdentifier &theTargetID)
Definition: OTWallet.cpp:1182
#define OT_ASSERT(x)
Definition: Assert.hpp:150
EXPORT OTLedger * LoadInbox(OTPseudonym &nym) const
Definition: OTAccount.cpp:225
static EXPORT const OTString & Receipt()
Definition: OTFolders.cpp:355
bool InitClient(OTWallet &theWallet)
Need to call this before using.
Definition: OTClient.cpp:13758
EXPORT bool LoadNymboxFromString(const OTString &strBox)
Definition: OTLedger.cpp:497
EXPORT bool SetOutboxHash(const std::string &acct_id, const OTIdentifier &theInput)
EXPORT bool AddAcknowledgedNum(const OTString &strServerID, const int64_t &lRequestNum)
#define OT_ASSERT_MSG(x, s)
Definition: Assert.hpp:155
EXPORT bool RemoveAcknowledgedNum(OTPseudonym &SIGNER_NYM, const OTString &strServerID, const int64_t &lRequestNum, bool bSave)
OTPayment * GetInstrument(const OTPseudonym &theNym, const int32_t &nIndex, OTLedger &ledger)
Definition: Helpers.cpp:149
EXPORT BasketItem * At(uint32_t nIndex)
Definition: Basket.cpp:298
static EXPORT OTLedger * GenerateLedger(const OTIdentifier &theUserID, const OTIdentifier &theAcctID, const OTIdentifier &theServerID, ledgerType theType, bool bCreateFile=false)
Definition: OTLedger.cpp:946
OTLOG_IMPORT OTLogStream otInfo
OTString m_strOutboxHash
Definition: OTMessage.hpp:198
OTASCIIArmor m_ascInReferenceTo
Definition: OTMessage.hpp:211
ledgerType GetType() const
Definition: OTLedger.hpp:212
EXPORT bool GetData(OTData &theData, bool bLineBreaks=true) const
EXPORT bool SaveWallet(const char *szFilename=nullptr)
Definition: OTWallet.cpp:1566
static EXPORT bool EraseActiveCronReceipt(const int64_t &lTransactionNum, const OTIdentifier &nymID, const OTIdentifier &serverID)
Definition: OTCronItem.cpp:344
virtual EXPORT void GetIdentifier(OTIdentifier &theIdentifier) const
Definition: OTContract.cpp:317
#define OT_FAIL
Definition: Assert.hpp:139
bool AcceptEntireInbox(OTLedger &theInbox, const OTIdentifier &theServerID, const OTServerContract &theServerContract, OTPseudonym &theNym, const OTMessage &theMessage, const OTAccount &theAccount)
Definition: OTClient.cpp:1158
EXPORT bool SaveNymbox(OTIdentifier *pNymboxHash=nullptr)
Definition: OTLedger.cpp:801
EXPORT bool SetRecentHash(const std::string &server_id, const OTIdentifier &theInput)
const OTIdentifier & GetCurrencyID() const
Definition: OTTrade.hpp:259
EXPORT bool At(uint32_t index, char &c) const
Definition: OTString.cpp:1025
static EXPORT const OTString & Market()
Definition: OTFolders.cpp:319
bool GetServerID(OTIdentifier &theID) const
OTString m_strServerID
Definition: OTMessage.hpp:191
EXPORT void SetNote(const OTString &theStr)
Definition: OTItem.cpp:1413
EXPORT bool GetNymboxHash(const std::string &server_id, OTIdentifier &theOutput) const
EXPORT bool IssueCheque(const int64_t &lAmount, const int64_t &lTransactionNum, const time64_t &VALID_FROM, const time64_t &VALID_TO, const OTIdentifier &SENDER_ACCT_ID, const OTIdentifier &SENDER_USER_ID, const OTString &strMemo, const OTIdentifier *pRECIPIENT_USER_ID=nullptr)
Definition: OTCheque.cpp:322
EXPORT int64_t GetReferenceToNum() const
EXPORT bool SetTempValues()
Definition: OTPayment.cpp:196
EXPORT bool SaveBoxReceipt(int64_t lLedgerType)
OTLOG_IMPORT OTLogStream otWarn
OTString m_strNymboxHash
Definition: OTMessage.hpp:194
void ProcessWithdrawalResponse(OTTransaction &theTransaction, const OTServerConnection &theConnection, const OTMessage &theReply) const
Definition: OTClient.cpp:3565
EXPORT void HarvestClosingNumbers(OTPseudonym &theNym, const OTIdentifier &theServerID, bool bSave=true)
Definition: Basket.cpp:187
EXPORT const char * Get() const
Definition: OTString.cpp:1045
EXPORT bool EraseValueByKey(std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:2488
EXPORT const void * GetPayloadPointer() const
Definition: OTPayload.cpp:318
virtual EXPORT bool SignContract(const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr)
Definition: OTContract.cpp:484
transactionType GetType() const
int32_t GetItemCount() const
OTLOG_IMPORT OTLogStream otErr
void SetExchangingIn(bool bDirection)
Definition: Basket.hpp:244
OTItem::itemStatus GetStatus() const
Definition: OTItem.hpp:459
#define OT_TIME_MINUTE_IN_SECONDS
Definition: Common.hpp:178
EXPORT int64_t GetReceiptAmount()
virtual bool VerifyAccount(const OTPseudonym &theNym)
EXPORT OTPacker * GetPacker(PackType ePackType=OTDB_DEFAULT_PACKER)
Definition: OTStorage.cpp:2103
EXPORT int64_t GetTransactionNum() const
std::map< std::string, std::string > the_map
Definition: OTStorage.hpp:981
void OnServerResponseToGetRequestNumber(int64_t lNewRequestNumber) const
EXPORT void SetReferenceToNum(int64_t lTransactionNum)
virtual PackedBuffer * CreateBuffer()=0
EXPORT bool VerifyAny(const OTNumList &rhs) const
Definition: OTNumList.cpp:367
OTString m_strRequestNum
Definition: OTMessage.hpp:207
EXPORT bool SavePurse(const char *szServerID=nullptr, const char *szUserID=nullptr, const char *szAssetTypeID=nullptr)
Definition: Purse.cpp:889
OTASCIIArmor m_ascPayload
Definition: OTMessage.hpp:214
bool IsAbbreviated() const
void ProcessIncomingTransactions(OTServerConnection &theConnection, OTMessage &theReply) const
Definition: OTClient.cpp:2056
OTPseudonym * GetNym() const
EXPORT const OTIdentifier & GetConstID() const
EXPORT bool RemoveOutpaymentsByIndex(int32_t nIndex, bool bDeleteIt=true)
int32_t GetOutpaymentsIndexByTransNum(const OTPseudonym &nym, int64_t lTransNum)
Definition: Helpers.cpp:323
EXPORT void AddMail(OTMessage &theMessage)
EXPORT void GetString(OTString &theStr) const
OTString m_strCommand
Definition: OTMessage.hpp:189
OTIdentifier SUB_CONTRACT_ID
Definition: BasketItem.hpp:150
virtual EXPORT bool VerifySignature(const OTPseudonym &theNym, const OTPasswordData *pPWData=nullptr) const
Definition: OTContract.cpp:818
const OTIdentifier & GetRecipientUserID() const
const OTIdentifier & GetSenderAcctID() const
const time64_t & GetLastProcessDate() const
Definition: OTCronItem.hpp:279
virtual EXPORT bool VerifyContract()
Definition: OTContract.cpp:330
virtual EXPORT bool VerifyContractID() const
EXPORT bool GetServer(int32_t iIndex, OTIdentifier &THE_ID, OTString &THE_NAME)
Definition: OTWallet.cpp:377
EXPORT bool Exists(std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:584
EXPORT bool LoadPurse(const char *szServerID=nullptr, const char *szUserID=nullptr, const char *szAssetTypeID=nullptr)
Definition: Purse.cpp:832
EXPORT bool SaveCredentialList()
static EXPORT Token * TokenFactory(OTString strInput)
Definition: Token.cpp:518
EXPORT Storable * DecodeObject(StoredObjectType theObjectType, std::string strInput)
Definition: OTStorage.cpp:830
static EXPORT const OTString & RecordBox()
Definition: OTFolders.cpp:359
OTItem::itemType GetType() const
Definition: OTItem.hpp:467
const OTIdentifier & GetUserID() const
EXPORT bool GetString(OTString &theData, bool bLineBreaks=true) const
int64_t OTTimeGetSecondsFromTime(time64_t time)
Definition: Common.hpp:230
EXPORT void AddClosingTransactionNo(const int64_t &lClosingTransactionNo)
Definition: OTCronItem.cpp:693
EXPORT bool LoadContractFromString(const OTString &theStr)
EXPORT bool StoreObject(Storable &theContents, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:759
#define OT_TIME_ZERO
Definition: Common.hpp:180
EXPORT int64_t GetIssuedNum(const OTIdentifier &theServerID, int32_t nIndex) const
EXPORT OTMessage * GetOutpaymentsByIndex(int32_t nIndex) const
EXPORT OTServerContract * GetServerContract(const OTIdentifier &SERVER_ID)
Definition: OTWallet.cpp:667
EXPORT bool SaveAccount()
Definition: OTAccount.cpp:379
void GetTypeString(OTString &strType) const
Definition: OTItem.hpp:504
virtual EXPORT void CalculateContractID(OTIdentifier &newID) const
Definition: OTContract.cpp:367
EXPORT size_t GetMasterCredentialCount() const
const OTIdentifier & GetRecipientUserID() const
Definition: OTCheque.hpp:176
OTString m_strNymPublicKey
Definition: OTMessage.hpp:202
EXPORT bool AddIssuedNum(const OTString &strServerID, const int64_t &lTransNum)
listOfItems & GetItemList()
EXPORT bool AddNewMasterCredential(OTString &strOutputMasterCredID, const OTString *pstrSourceForNymID=nullptr, int32_t nBits=1024, const OTString::Map *pmapPrivate=nullptr, const OTString::Map *pmapPublic=nullptr, const OTPasswordData *pPWData=nullptr, bool bChangeNymID=false)
static EXPORT OTAccount * LoadExistingAccount(const OTIdentifier &accountId, const OTIdentifier &serverId)
Definition: OTAccount.cpp:480
virtual EXPORT void Release()
Definition: OTString.cpp:765
EXPORT void AddSubContract(const OTIdentifier &SUB_CONTRACT_ID, int64_t lMinimumTransferAmount)
Definition: Basket.cpp:265
EXPORT bool VerifyAcknowledgedNum(const OTString &strServerID, const int64_t &lRequestNum) const
uint32_t GetSize() const
Definition: OTData.hpp:174
const OTIdentifier & GetRealServerID() const
EXPORT bool ProcessInBuffer(const OTMessage &) const
EXPORT bool UnRegisterAtServer(const OTString &strServerID)
static EXPORT OTCronItem * NewCronItem(const OTString &strCronItem)
Definition: OTCronItem.cpp:165
EXPORT void AddItem(OTItem &theItem)
EXPORT bool SaveSignedNymfile(OTPseudonym &SIGNER_NYM)
EXPORT void AddAccount(const OTAccount &theAcct)
Definition: OTWallet.cpp:561
EXPORT int32_t Count() const
Definition: Basket.cpp:305
EXPORT OTTransaction * GetTransaction(OTTransaction::transactionType theType)
Definition: OTLedger.cpp:1215
EXPORT void IncrementRequestNum(OTPseudonym &SIGNER_NYM, const OTString &strServerID)
const int64_t & GetPriceLimit() const
Definition: OTOffer.hpp:282
EXPORT bool IsEmpty() const
Definition: Purse.cpp:1646
EXPORT void SetName(const OTString &strName)
Definition: OTContract.hpp:364
EXPORT const mapOfTransactions & GetTransactionMap() const
Definition: OTLedger.cpp:1160
EXPORT bool SaveInbox(OTIdentifier *pInboxHash=nullptr)
Definition: OTLedger.cpp:836
EXPORT bool StoreObject(Storable &theContents, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:2301
EXPORT Token * Pop(OTNym_or_SymmetricKey theOwner)
Definition: Purse.cpp:1473