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

#include <OTClient.hpp>

Collaboration diagram for opentxs::OTClient:

Public Types

enum  OT_CLIENT_CMD_TYPE {
  checkServerID, createUserAccount, deleteUserAccount, checkUser,
  sendUserMessage, getRequest, issueAssetType, createAccount,
  issueBasket, exchangeBasket, getTransactionNum, getNymbox,
  getInbox, getOutbox, processNymbox, processEntireNymbox,
  processInbox, processEntireInbox, getAccount, getContract,
  getMint, writeCheque, signContract, proposePaymentPlan,
  confirmPaymentPlan, notarizeTransfer, notarizeWithdrawal, withdrawVoucher,
  notarizeDeposit, notarizePurse, notarizeCheque, marketOffer,
  paymentPlan, setAccountName, setNymName, setServerName,
  setAssetName, badID
}
 

Public Member Functions

int32_t CalcReturnVal (const int64_t &lRequestNumber)
 
bool IsRunningAsScript () const
 
void SetRunningAsScript ()
 
OTMessageBufferGetMessageBuffer ()
 
OTMessageOutbufferGetMessageOutbuffer ()
 
EXPORT bool SetFocusToServerAndNym (OTServerContract &theServerContract, OTPseudonym &theNym, TransportCallback *pCallback) const
 
bool ConnectToTheFirstServerOnList (const OTPseudonym &theNym, const OTString &strCA_FILE, const OTString &strKEY_FILE, const OTString &strKEY_PASSWORD) const
 
 OTClient ()
 
 ~OTClient ()
 
bool InitClient (OTWallet &theWallet)
 Need to call this before using. More...
 
void ProcessMessageOut (const char *buf, int32_t *pnExpectReply) const
 
void ProcessMessageOut (const OTMessage &theMessage)
 
bool ProcessInBuffer (const OTMessage &theServerReply) const
 
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)
 
bool ProcessServerReply (OTMessage &theReply, OTLedger *pNymbox=nullptr)
 
void ProcessIncomingTransactions (OTServerConnection &theConnection, OTMessage &theReply) const
 
void ProcessWithdrawalResponse (OTTransaction &theTransaction, const OTServerConnection &theConnection, const OTMessage &theReply) const
 
void ProcessDepositResponse (OTTransaction &theTransaction, const OTServerConnection &theConnection, const OTMessage &theReply) const
 
void ProcessPayDividendResponse (OTTransaction &theTransaction, const OTServerConnection &theConnection, const OTMessage &theReply) const
 
bool AcceptEntireInbox (OTLedger &theInbox, const OTIdentifier &theServerID, const OTServerContract &theServerContract, OTPseudonym &theNym, const OTMessage &theMessage, const OTAccount &theAccount)
 
bool AcceptEntireNymbox (OTLedger &theNymbox, const OTIdentifier &theServerID, const OTServerContract &theServerContract, OTPseudonym &theNym, OTMessage &theMessage)
 

Public Attributes

OTServerConnectionm_pConnection
 
bool m_bInitialized
 

Detailed Description

Definition at line 157 of file OTClient.hpp.

Member Enumeration Documentation

Enumerator
checkServerID 
createUserAccount 
deleteUserAccount 
checkUser 
sendUserMessage 
getRequest 
issueAssetType 
createAccount 
issueBasket 
exchangeBasket 
getTransactionNum 
getNymbox 
getInbox 
getOutbox 
processNymbox 
processEntireNymbox 
processInbox 
processEntireInbox 
getAccount 
getContract 
getMint 
writeCheque 
signContract 
proposePaymentPlan 
confirmPaymentPlan 
notarizeTransfer 
notarizeWithdrawal 
withdrawVoucher 
notarizeDeposit 
notarizePurse 
notarizeCheque 
marketOffer 
paymentPlan 
setAccountName 
setNymName 
setServerName 
setAssetName 
badID 

Definition at line 200 of file OTClient.hpp.

200  {
201  checkServerID, // Your public key is sent along with this message so the
202  // server can reply to
203  // you even without your being a registered user. Other than these top
204  // two commands,
205  // all other commands can only be executed by registered users.
206  //
207  // The server ID is a hash of the server contract. The signature on the
208  // contract
209  // can be verified by a public key that appears in a standard section of
210  // any server
211  // contract. The URL/port information is also derived from the contract.
212  //
213  // Simply by importing the server contract into your wallet, you are
214  // able to connect
215  // to it and encrypt all of your communications to it.
216  //
217  // Thus, the check server ID command really just confirms what you
218  // should already know...
219  // Your wallet still wants to see that the server agrees with the server
220  // ID, and that
221  // the server is able to read messages that were encrypted to the public
222  // key in the
223  // contract, and that the server is able to sign all of its future
224  // correspondence with
225  // the same public key.
226  //
227  // It is the server operator's responsibility to secure the domain name
228  // and web host
229  // that users will connect to when they import the contract, as well as
230  // the private
231  // key that matches the public key from the contract.
232  createUserAccount, // Create user account on a specific server, with
233  // public key. User ID will be hash of said public
234  // key.
235  deleteUserAccount, // Delete user account from a specific server.
236  checkUser, // Request a user's public key based on User ID included with
237  // the request.
238  // (If you want to send him cash or a check, your wallet will encrypt
239  // portions
240  // of the tokens, etc, to the Nym of the recipient.)
241  sendUserMessage, // Send a message to another user, encrypted to his
242  // public key and dropped into his nymbox.
243  getRequest, // Get the next request number from the server (for this
244  // user). Most requests must be
245  // accompanied by a request number, which increments for each Nym with
246  // each request.
247  issueAssetType, // Upload a currency contract to the server and create
248  // an asset ID from a hash of that.
249  // contract. Also creates an issuer account for that asset ID. This ONLY
250  // works if public
251  // key of the user matches the contract key found in the currency
252  // contract, AND if the
253  // contract is signed by the same key.
254  createAccount, // Create an asset account for a certain serverID,
255  // UserID, and Asset Type ID.
256  // These accounts are where users actually store their digital assets of
257  // various
258  // types. Account files are stored on user's computer, signed by notary
259  // server.
260  // Server also maintains its own copy. Users can create an unlimited
261  // number of accounts
262  // for any asset type that they choose.
263  issueBasket, // Create a basket account, which is like an issuer
264  // account, but based on a basket of
265  // other asset types. This way, users can trade with what is apparently
266  // a single currency,
267  // when in fact the issuence is delegated and distributed across
268  // multiple issuers.
269  exchangeBasket, // Use this to exchange assets in and out of a basket
270  // currency.
271  getTransactionNum, // Every transaction requires a transaction number.
272  // If your wallet doesn't have one,
273  // then here it can request the server to send one over. (Or several.)
274  getNymbox, // Grab a copy of my nymbox (contains messages and new
275  // transaction numbers)
276  getInbox, // Grab a copy of my inbox from the server so I can decide
277  // what to do with it.
278  getOutbox, // Grab a copy of my outbox from the server so I can decide
279  // what to do with it.
280  processNymbox, // Used by AcceptEntireNymbox() as it's setting
281  // everything up.
282  processEntireNymbox, // Instruct the server what to do with the various
283  // items sitting in my nymbox. (per user)
284  processInbox, // Instruct the server what to do with the various items
285  // sitting in my inbox. (per asset acct)
286  processEntireInbox, // Just accept everything in the server (used in the
287  // command line test client.)
288  getAccount, // Grab the server's copy of my asset account file, in case
289  // mine is lost.
290  getContract, // Grab the server's copy of any asset contract. Input is
291  // the asset type ID.
292  getMint, // Grab the server's copy of any mint based on Asset ID. (For
293  // blinded tokens.)
294  writeCheque, // Write a cheque. (Actually sends no message to the server
295  // -- returns false.)
296  signContract, // Sign a contract. (Sends no message to the server.)
297  proposePaymentPlan, // (Merchant) Propose a payment plan. (Sends no
298  // message to the server.)
299  confirmPaymentPlan, // (Customer) Confirm a payment plan. (Sends no
300  // message to the server.)
301  notarizeTransfer, // Request the server to transfer from one account to
302  // another.
303  notarizeWithdrawal, // Request the server to withdraw from an asset
304  // account and return digital cash tokens to the
305  // wallet.
306  withdrawVoucher, // Request the server to withdraw from an asset account
307  // and issue a voucher (cashier's cheque)
308  notarizeDeposit, // Request the server to accept some digital cash and
309  // deposit it to an asset account.
310  notarizePurse, // Same as the above, but sends an entire purse of tokens
311  // at once instead of sending individual tokens.
312  notarizeCheque, // Deposit like the above, but deposits a cheque instead
313  // of cash tokens.
314  marketOffer, // Create an Offer object and add it to one of the server's
315  // Market objects.
316  // This will also create a Trade object and add it to the server's Cron
317  // object.
318  // (The Trade provides the payment authorization for the Offer, as well
319  // as the rules
320  // for processing and expiring it.)
321  paymentPlan, // Send a payment plan to the server (request to activate
322  // one onto yourself, basically.)
323  // The test client will ask you to input the plan, which you must
324  // already have (like a cheque).
325  // The Payee must create it and sign it, then he sends it to the Payer,
326  // who uses this command
327  // to sign it and submit it to the server.
328  setAccountName, // For setting the client-side label on an asset
329  // account.
330  setNymName, // For setting the client-side label on a Nym.
331  setServerName, // For setting the client-side label on a server
332  // contract.
333  setAssetName, // For setting the client-side label on an asset contract.
334  badID
335  };

Constructor & Destructor Documentation

opentxs::OTClient::OTClient ( )

Definition at line 13773 of file OTClient.cpp.

13774  : m_pWallet(nullptr)
13775  , m_bRunningAsScript(false)
13776  , m_lMostRecentRequestNumber(0)
13777  , m_pConnection(nullptr)
13778  , m_bInitialized(false)
13779 {
13780 }
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357
opentxs::OTClient::~OTClient ( )

Definition at line 13782 of file OTClient.cpp.

13783 {
13784  if (nullptr != m_pConnection) delete m_pConnection;
13785  m_pConnection = nullptr;
13786 }
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357

Member Function Documentation

bool opentxs::OTClient::AcceptEntireInbox ( OTLedger theInbox,
const OTIdentifier theServerID,
const OTServerContract theServerContract,
OTPseudonym theNym,
const OTMessage theMessage,
const OTAccount theAccount 
)

Without regard to WHAT those transactions ARE that are in my inbox, just process and accept them all!!! (This is AUTO-ACCEPT functionality built into the test client and not the library itself.)

Definition at line 1158 of file OTClient.cpp.

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())) {
1284  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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 
1444  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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  {
1498  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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 }
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
OTLOG_IMPORT OTLogStream otOut
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
static EXPORT OTItem * CreateItemFromTransaction(const OTTransaction &theOwner, OTItem::itemType theType, const OTIdentifier *pDestinationAcctID=nullptr)
Definition: OTItem.cpp:1451
#define OT_ASSERT(x)
Definition: Assert.hpp:150
static EXPORT bool EraseActiveCronReceipt(const int64_t &lTransactionNum, const OTIdentifier &nymID, const OTIdentifier &serverID)
Definition: OTCronItem.cpp:344
OTLOG_IMPORT OTLogStream otWarn
OTLOG_IMPORT OTLogStream otErr
bool opentxs::OTClient::AcceptEntireNymbox ( OTLedger theNymbox,
const OTIdentifier theServerID,
const OTServerContract theServerContract,
OTPseudonym theNym,
OTMessage theMessage 
)

This is standard behavior for the Nymbox (NOT the inbox.) That is, to just accept everything there.

Definition at line 259 of file OTClient.cpp.

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 
347  OTTransaction* pAcceptTransaction = OTTransaction::GenerateTransaction(
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())) {
410  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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())) {
467  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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())) {
495  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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  }
577  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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  {
638  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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
820  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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());
914  OTItem* pAcceptItem = OTItem::CreateItemFromTransaction(
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 }
OTLOG_IMPORT OTLogStream otLog4
static EXPORT OTTransaction * GenerateTransaction(const OTIdentifier &theUserID, const OTIdentifier &theAccountID, const OTIdentifier &theServerID, transactionType theType, int64_t lTransactionNum=0)
OTLOG_IMPORT OTLogStream otOut
bool ProcessServerReply(OTMessage &theReply, OTLedger *pNymbox=nullptr)
Definition: OTClient.cpp:3764
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
static EXPORT OTItem * CreateItemFromTransaction(const OTTransaction &theOwner, OTItem::itemType theType, const OTIdentifier *pDestinationAcctID=nullptr)
Definition: OTItem.cpp:1451
#define OT_ASSERT(x)
Definition: Assert.hpp:150
#define OT_ASSERT_MSG(x, s)
Definition: Assert.hpp:155
OTLOG_IMPORT OTLogStream otInfo
static EXPORT bool EraseActiveCronReceipt(const int64_t &lTransactionNum, const OTIdentifier &nymID, const OTIdentifier &serverID)
Definition: OTCronItem.cpp:344
OTLOG_IMPORT OTLogStream otWarn
OTLOG_IMPORT OTLogStream otErr
int32_t opentxs::OTClient::CalcReturnVal ( const int64_t &  lRequestNumber)

Definition at line 173 of file OTClient.cpp.

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 }
bool opentxs::OTClient::ConnectToTheFirstServerOnList ( const OTPseudonym theNym,
const OTString strCA_FILE,
const OTString strKEY_FILE,
const OTString strKEY_PASSWORD 
) const

used for testing. Once the wallet is loaded, we are assuming there is at least one server contract in the wallet, and we are asking the wallet to look it up, find the hostname and port inside that contract, and establish a connection to the server.

Whereas in a nice user interface, you would loop through all the servers in the wallet and display them in a nice list on the screen, and the user could just click on one, and you would just call Wallet.Connect(ServerID) and do your thing.

Definition at line 13722 of file OTClient.cpp.

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 }
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357
EXPORT bool Connect(const OTPseudonym &, const OTServerContract &, const OTString &, const OTString &, const OTString &) const
#define OT_ASSERT(x)
Definition: Assert.hpp:150
EXPORT bool GetServer(int32_t iIndex, OTIdentifier &THE_ID, OTString &THE_NAME)
Definition: OTWallet.cpp:377
EXPORT OTServerContract * GetServerContract(const OTIdentifier &SERVER_ID)
Definition: OTWallet.cpp:667
OTMessageBuffer& opentxs::OTClient::GetMessageBuffer ( )
inline

Definition at line 359 of file OTClient.hpp.

360  {
361  return m_MessageBuffer;
362  }
OTMessageOutbuffer& opentxs::OTClient::GetMessageOutbuffer ( )
inline

Definition at line 363 of file OTClient.hpp.

364  {
365  return m_MessageOutbuffer;
366  }
bool opentxs::OTClient::InitClient ( OTWallet theWallet)

Need to call this before using.

Definition at line 13758 of file OTClient.cpp.

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 }
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357
OTLOG_IMPORT OTLogStream otWarn
bool opentxs::OTClient::IsRunningAsScript ( ) const
inline

Definition at line 192 of file OTClient.hpp.

193  {
194  return m_bRunningAsScript;
195  }
void opentxs::OTClient::ProcessDepositResponse ( OTTransaction theTransaction,
const OTServerConnection theConnection,
const OTMessage theReply 
) const

Definition at line 3287 of file OTClient.cpp.

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 }
static EXPORT OTTransactionType * TransactionFactory(OTString strInput)
OTLOG_IMPORT OTLogStream otOut
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
#define OT_ASSERT(x)
Definition: Assert.hpp:150
OTPayment * GetInstrument(const OTPseudonym &theNym, const int32_t &nIndex, OTLedger &ledger)
Definition: Helpers.cpp:149
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 otWarn
EXPORT const char * Get() const
Definition: OTString.cpp:1045
OTLOG_IMPORT OTLogStream otErr
EXPORT bool Exists(std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:584
static EXPORT const OTString & RecordBox()
Definition: OTFolders.cpp:359
bool opentxs::OTClient::ProcessInBuffer ( const OTMessage theServerReply) const

Definition at line 247 of file OTClient.cpp.

248 {
250  nullptr != m_pConnection,
251  "OTClient::ProcessInBuffer: ASSERT: nullptr != m_pConnection\n");
252 
253  return m_pConnection->ProcessInBuffer(theServerReply);
254 }
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357
#define OT_ASSERT_MSG(x, s)
Definition: Assert.hpp:155
EXPORT bool ProcessInBuffer(const OTMessage &) const
void opentxs::OTClient::ProcessIncomingTransactions ( OTServerConnection theConnection,
OTMessage theReply 
) const

We have received the server reply (ProcessServerReply) which has vetted it and determined that it is legitimate and safe, and that it is a reply to a transaction request.

At that point, this function is called to open the reply, go through the transaction responses, and potentially grab any bearer certificates that are inside and save them in a purse somewhere. (And do any other necessary processing on that reply.)

Also: Need to call this function after Nymbox notices of old server replies (to prevent sync issues). But what if already processed? Call pNym-VerifyIssuedNum(strServerID, pTrans->GetTransactionNum() and if you discover that it's already been removed, then discard it (or save it in your auto-receipt storage. But if you discover that the number is STILL issued to you, the simply call the below function, the same as you would have normally if you had received the server reply in the first place! That way transaction sync issues become impossible. SOLUTION: bool OTClient::ProcessServerReply(OTMessage& theReply) Any message deemed important enough to have a notice containing the reply dropped into my nymbox, I will just take that message and pass it to ProcessServerReply(), which will then call THIS function (ProcessIncomingTransactions) where appropriate, and THIS function should therefore then be smart enough to ignore transactions that aren't VERIFIED as issued numbers on this Nym still! (An easy enough test for determining whether it's already been processed...) If it's not on the issued list, then skip it! I must have processed it already.

Definition at line 2056 of file OTClient.cpp.

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 =
2525  pNym->RemoveOutpaymentsByIndex(
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
2753  .GetAllTransactionNumbers(
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
3012  ->SetReferenceToNum(
3013  lNymOpeningNumber); // Referencing myself here. We'll see how it works out.
3014  pNewTransaction
3015  ->SetReferenceString(
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 }
EXPORT bool StorePlainString(std::string strContents, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:698
void ProcessDepositResponse(OTTransaction &theTransaction, const OTServerConnection &theConnection, const OTMessage &theReply) const
Definition: OTClient.cpp:3287
static EXPORT OTTransaction * GenerateTransaction(const OTIdentifier &theUserID, const OTIdentifier &theAccountID, const OTIdentifier &theServerID, transactionType theType, int64_t lTransactionNum=0)
static EXPORT const OTString & PaymentInbox()
Definition: OTFolders.cpp:339
static EXPORT OTTransactionType * TransactionFactory(OTString strInput)
void ProcessPayDividendResponse(OTTransaction &theTransaction, const OTServerConnection &theConnection, const OTMessage &theReply) const
Definition: OTClient.cpp:3252
static EXPORT const char * PathSeparator()
Definition: OTLog.cpp:408
OTLOG_IMPORT OTLogStream otOut
OTLOG_IMPORT OTLogStream otLog3
#define OT_ASSERT(x)
Definition: Assert.hpp:150
static EXPORT const OTString & Receipt()
Definition: OTFolders.cpp:355
#define OT_ASSERT_MSG(x, s)
Definition: Assert.hpp:155
OTPayment * GetInstrument(const OTPseudonym &theNym, const int32_t &nIndex, OTLedger &ledger)
Definition: Helpers.cpp:149
OTLOG_IMPORT OTLogStream otInfo
void ProcessWithdrawalResponse(OTTransaction &theTransaction, const OTServerConnection &theConnection, const OTMessage &theReply) const
Definition: OTClient.cpp:3565
EXPORT const char * Get() const
Definition: OTString.cpp:1045
OTLOG_IMPORT OTLogStream otErr
int32_t GetOutpaymentsIndexByTransNum(const OTPseudonym &nym, int64_t lTransNum)
Definition: Helpers.cpp:323
EXPORT bool Exists(std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:584
static EXPORT const OTString & RecordBox()
Definition: OTFolders.cpp:359
static EXPORT OTCronItem * NewCronItem(const OTString &strCronItem)
Definition: OTCronItem.cpp:165
void opentxs::OTClient::ProcessMessageOut ( const char *  buf,
int32_t *  pnExpectReply 
) const

Definition at line 190 of file OTClient.cpp.

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 }
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357
void ProcessMessageOut(const char *buf, const int32_t *pnExpectReply)
void opentxs::OTClient::ProcessMessageOut ( const OTMessage theMessage)

Definition at line 203 of file OTClient.cpp.

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 }
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357
EXPORT void AddSentMessage(OTMessage &message)
void ProcessMessageOut(const char *buf, const int32_t *pnExpectReply)
#define OT_ASSERT(x)
Definition: Assert.hpp:150
#define OT_ASSERT_MSG(x, s)
Definition: Assert.hpp:155
void opentxs::OTClient::ProcessPayDividendResponse ( OTTransaction theTransaction,
const OTServerConnection theConnection,
const OTMessage theReply 
) const

Definition at line 3252 of file OTClient.cpp.

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 }
OTLOG_IMPORT OTLogStream otOut
#define OT_ASSERT(x)
Definition: Assert.hpp:150
bool opentxs::OTClient::ProcessServerReply ( OTMessage theReply,
OTLedger pNymbox = nullptr 
)

We have just received a message from the server. Find out what it is and do the appropriate processing. Perhaps we just tried to create an account – this could be our new account! Let's make sure we receive it and save it to disk somewhere.

PS... The Client TAKES OWNERSHIP of this message (adding it to a message buffer) and will store it until the buffer is flushed, or until the messages are popped back off later for processing by the client API. THEREFORE – theReply MUST be allocated on the heap, and is only passed in as a reference here in order to make sure it's real.

returns true/false on whether or not the reply was actually verified and processed, versus whether

Definition at line 3764 of file OTClient.cpp.

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  }
3800  OTMessage* pSentMsg = GetMessageOutbuffer().GetSentMessage(
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<
5445  OTDB::TradeListNym*>(
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(
5744  OTTransactionType::
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  //
5951  pNym->HarvestTransactionNumbers(
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 =
6624  pNym->RemoveOutpaymentsByIndex(
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
6980  .GetAllTransactionNumbers(
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
7395  ->SetReferenceToNum(
7396  lNymOpeningNumber); // Referencing myself here. We'll see how it works out.
7397  pNewTransaction
7398  ->SetReferenceString(
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 }
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
std::map< std::string, std::string > Map
Definition: OTString.hpp:162
EXPORT Storable * CreateObject(StoredObjectType eType)
Definition: OTStorage.cpp:530
static EXPORT int64_t StringToLong(const std::string &number)
Definition: OTString.cpp:677
EXPORT OTMessage * GetSentMessage(const int64_t &requestNum, const OTString &serverId, const OTString &nymId)
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
static EXPORT Mint * MintFactory()
Definition: Mint.cpp:154
static EXPORT const OTString & Pubcred()
Definition: OTFolders.cpp:343
EXPORT bool RemoveSentMessage(const int64_t &requestNum, const OTString &serverId, const OTString &nymId)
std::string to_string(const T &t)
static EXPORT const OTString & PaymentInbox()
Definition: OTFolders.cpp:339
static EXPORT OTTransactionType * TransactionFactory(OTString strInput)
static EXPORT const char * PathSeparator()
Definition: OTLog.cpp:408
static EXPORT const OTString & Nym()
Definition: OTFolders.cpp:327
OTLOG_IMPORT OTLogStream otOut
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
EXPORT bool GetAccount(int32_t iIndex, OTIdentifier &THE_ID, OTString &THE_NAME)
Definition: OTWallet.cpp:431
int64_t time64_t
Definition: Common.hpp:209
static EXPORT const OTString & Inbox()
Definition: OTFolders.cpp:315
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357
static EXPORT const OTString & Contract()
Definition: OTFolders.cpp:303
EXPORT Storable * QueryObject(StoredObjectType theObjectType, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:788
EXPORT void Push(OTMessage &message)
OTMessageOutbuffer & GetMessageOutbuffer()
Definition: OTClient.hpp:363
EXPORT bool RemoveAccount(const OTIdentifier &theTargetID)
Definition: OTWallet.cpp:1182
#define OT_ASSERT(x)
Definition: Assert.hpp:150
static EXPORT const OTString & Receipt()
Definition: OTFolders.cpp:355
#define OT_ASSERT_MSG(x, s)
Definition: Assert.hpp:155
OTPayment * GetInstrument(const OTPseudonym &theNym, const int32_t &nIndex, OTLedger &ledger)
Definition: Helpers.cpp:149
OTLOG_IMPORT OTLogStream otInfo
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
#define OT_FAIL
Definition: Assert.hpp:139
static EXPORT const OTString & Market()
Definition: OTFolders.cpp:319
OTLOG_IMPORT OTLogStream otWarn
EXPORT const char * Get() const
Definition: OTString.cpp:1045
OTLOG_IMPORT OTLogStream otErr
void ProcessIncomingTransactions(OTServerConnection &theConnection, OTMessage &theReply) const
Definition: OTClient.cpp:2056
int32_t GetOutpaymentsIndexByTransNum(const OTPseudonym &nym, int64_t lTransNum)
Definition: Helpers.cpp:323
EXPORT bool Exists(std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:584
EXPORT Storable * DecodeObject(StoredObjectType theObjectType, std::string strInput)
Definition: OTStorage.cpp:830
static EXPORT const OTString & RecordBox()
Definition: OTFolders.cpp:359
EXPORT bool StoreObject(Storable &theContents, std::string strFolder, std::string oneStr="", std::string twoStr="", std::string threeStr="")
Definition: OTStorage.cpp:759
static EXPORT OTAccount * LoadExistingAccount(const OTIdentifier &accountId, const OTIdentifier &serverId)
Definition: OTAccount.cpp:480
static EXPORT OTCronItem * NewCronItem(const OTString &strCronItem)
Definition: OTCronItem.cpp:165
EXPORT void AddAccount(const OTAccount &theAcct)
Definition: OTWallet.cpp:561
int32_t opentxs::OTClient::ProcessUserCommand ( OTClient::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 
)

This function sets up "theMessage" so that it is ready to be sent out to the server. If you want to set up a checkServerID command and send it to the server, then you just call this to get the OTMessage object all set up and ready to be sent. returns -1 if error, don't send message. returns 0 if NO error, but still, don't send message. returns 1 if message is sent but there's not request number returns >0 for processInbox, containing the number that was there before processing. returns >0 for nearly everything else, containing the request number itself.

Definition at line 8905 of file OTClient.cpp.

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...)
9533  OTItem* pItem = OTItem::CreateItemFromTransaction(
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
10042  OTTransaction* pTransaction = OTTransaction::GenerateTransaction(
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...)
10048  OTItem* pItem = OTItem::CreateItemFromTransaction(
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
11085  OTTransaction* pTransaction = OTTransaction::GenerateTransaction(
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
11482  OTTransaction* pTransaction = OTTransaction::GenerateTransaction(
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...)
11767  OTItem* pItem = OTItem::CreateItemFromTransaction(
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...)
12065  OTItem* pItem = OTItem::CreateItemFromTransaction(
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
12277  OTTransaction* pTransaction = OTTransaction::GenerateTransaction(
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...)
12702  OTItem* pItem = OTItem::CreateItemFromTransaction(
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...)
13562  OTItem* pItem = OTItem::CreateItemFromTransaction(
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 }
std::map< std::string, std::string > Map
Definition: OTString.hpp:162
static EXPORT OTAsymmetricKey * KeyFactory()
EXPORT Storable * CreateObject(StoredObjectType eType)
Definition: OTStorage.cpp:530
static EXPORT OTTransaction * GenerateTransaction(const OTIdentifier &theUserID, const OTIdentifier &theAccountID, const OTIdentifier &theServerID, transactionType theType, int64_t lTransactionNum=0)
int32_t CalcReturnVal(const int64_t &lRequestNumber)
Definition: OTClient.cpp:173
static EXPORT Mint * MintFactory()
Definition: Mint.cpp:154
EXPORT OTAccount * GetAccountPartialMatch(std::string PARTIAL_ID)
Definition: OTWallet.cpp:613
#define OT_TIME_HOUR_IN_SECONDS
Definition: Common.hpp:177
EXPORT OTAssetContract * GetAssetContract(const OTIdentifier &theContractID)
Definition: OTWallet.cpp:1203
time64_t OTTimeAddTimeInterval(time64_t lhs, int64_t rhs)
Definition: Common.hpp:238
EXPORT void AddPendingWithdrawal(const Purse &thePurse)
Definition: OTWallet.cpp:232
OTLOG_IMPORT OTLogStream otOut
void SetNymName(const OTString &strName)
OTLOG_IMPORT OTLogStream otLog3
EXPORT bool GetAccount(int32_t iIndex, OTIdentifier &THE_ID, OTString &THE_NAME)
Definition: OTWallet.cpp:431
time64_t OTTimeGetTimeFromSeconds(int64_t seconds)
Definition: Common.hpp:215
int64_t time64_t
Definition: Common.hpp:209
static EXPORT const OTString & Contract()
Definition: OTFolders.cpp:303
#define OT_TIME_SIX_MONTHS_IN_SECONDS
Definition: Common.hpp:170
static EXPORT OTItem * CreateItemFromTransaction(const OTTransaction &theOwner, OTItem::itemType theType, const OTIdentifier *pDestinationAcctID=nullptr)
Definition: OTItem.cpp:1451
EXPORT std::string EncodeObject(Storable &theContents)
Definition: OTStorage.cpp:818
#define OT_TIME_DAY_IN_SECONDS
Definition: Common.hpp:176
static EXPORT Token * InstantiateAndGenerateTokenRequest(const Purse &thePurse, const OTPseudonym &theNym, Mint &theMint, int64_t lDenomination, int32_t nTokenCount=Token::GetMinimumPrototokenCount())
Definition: Token.cpp:1086
time64_t OTTimeGetCurrentTime()
Definition: Common.hpp:211
EXPORT OTPseudonym * GetNymByID(const OTIdentifier &NYM_ID)
Definition: OTWallet.cpp:275
bool AcceptEntireNymbox(OTLedger &theNymbox, const OTIdentifier &theServerID, const OTServerContract &theServerContract, OTPseudonym &theNym, OTMessage &theMessage)
Definition: OTClient.cpp:259
#define OT_ASSERT(x)
Definition: Assert.hpp:150
EXPORT bool SaveWallet(const char *szFilename=nullptr)
Definition: OTWallet.cpp:1566
bool AcceptEntireInbox(OTLedger &theInbox, const OTIdentifier &theServerID, const OTServerContract &theServerContract, OTPseudonym &theNym, const OTMessage &theMessage, const OTAccount &theAccount)
Definition: OTClient.cpp:1158
OTLOG_IMPORT OTLogStream otErr
#define OT_TIME_MINUTE_IN_SECONDS
Definition: Common.hpp:178
static EXPORT Token * TokenFactory(OTString strInput)
Definition: Token.cpp:518
int64_t OTTimeGetSecondsFromTime(time64_t time)
Definition: Common.hpp:230
#define OT_TIME_ZERO
Definition: Common.hpp:180
EXPORT OTServerContract * GetServerContract(const OTIdentifier &SERVER_ID)
Definition: OTWallet.cpp:667
EXPORT void SetName(const OTString &strName)
Definition: OTContract.hpp:364
void opentxs::OTClient::ProcessWithdrawalResponse ( OTTransaction theTransaction,
const OTServerConnection theConnection,
const OTMessage theReply 
) const

It's definitely a withdrawal, we just need to iterate through the items in the transaction and grab any cash tokens that are inside, to save inside a purse. Also want to display any vouchers.

Definition at line 3565 of file OTClient.cpp.

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 }
static EXPORT Mint * MintFactory()
Definition: Mint.cpp:154
OTLOG_IMPORT OTLogStream otOut
#define OT_ASSERT(x)
Definition: Assert.hpp:150
OTLOG_IMPORT OTLogStream otWarn
OTLOG_IMPORT OTLogStream otErr
bool opentxs::OTClient::SetFocusToServerAndNym ( OTServerContract theServerContract,
OTPseudonym theNym,
TransportCallback pCallback 
) const

Used in RPC mode (instead of Connect.) Whenever a message needs to be processed, this function is called first, in lieu of Connect(), so that the right pointers and IDs are in place for OTClient to do its thing.

Definition at line 13747 of file OTClient.cpp.

13750 {
13751  OT_ASSERT(nullptr != pCallback);
13752  OT_ASSERT(nullptr != m_pConnection);
13753 
13754  return m_pConnection->SetFocus(theNym, theServerContract, pCallback);
13755 }
bool SetFocus(OTPseudonym &theNym, OTServerContract &theServerContract, TransportCallback *pCallback)
OTServerConnection * m_pConnection
Definition: OTClient.hpp:357
#define OT_ASSERT(x)
Definition: Assert.hpp:150
void opentxs::OTClient::SetRunningAsScript ( )
inline

Definition at line 196 of file OTClient.hpp.

197  {
198  m_bRunningAsScript = true;
199  } // (default is false.)

Member Data Documentation

bool opentxs::OTClient::m_bInitialized

Definition at line 391 of file OTClient.hpp.

OTServerConnection* opentxs::OTClient::m_pConnection

Definition at line 357 of file OTClient.hpp.


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