// This constructor gets the string version of the ID passed in, // and sets that string on this object. (For when you need a string // version of an ID.) OTString::OTString(const OTIdentifier & theValue) : m_lLength(0), m_lPosition(0), m_strBuffer(NULL) { // Initialize(); if (theValue.GetSize() > 0) theValue.GetString(*this); }
// This constructor gets the string version of the ID passed in, // and sets that string on this object. (For when you need a string // version of an ID.) OTString::OTString(const OTIdentifier & theValue) { Initialize(); if (theValue.GetSize() > 0) theValue.GetString(*this); }
bool OTToken::RecordTokenAsSpent(OTString & theCleartextToken) { OTString strAssetID(GetAssetID()); // ---------------------------------------------------------------------------- // Calculate the filename (a hash of the Lucre cleartext token ID) OTIdentifier theTokenHash; theTokenHash.CalculateDigest(theCleartextToken); // Grab the new hash into a string (for use as a filename) OTString strTokenHash(theTokenHash); OTString strAssetFolder; strAssetFolder.Format("%s.%d", strAssetID.Get(), GetSeries()); // -------------------------------------------------------------------- // See if the spent token file ALREADY EXISTS... bool bTokenIsPresent = OTDB::Exists(OTLog::SpentFolder(), strAssetFolder.Get(), strTokenHash.Get()); // If so, we're trying to record a token that was already recorded... if (bTokenIsPresent) { OTLog::vError("OTToken::RecordTokenAsSpent: Trying to record token as spent," " but it was already recorded: %s%s%s%s%s\n", OTLog::SpentFolder(), OTLog::PathSeparator(), strAssetFolder.Get(), OTLog::PathSeparator(), strTokenHash.Get()); return false; } // ---------------------------------------------------------------------- // FINISHED: // We actually save the token itself into the file, which is named based // on a hash of the Lucre data. // The success of that operation is also now the success of this one. OTString strToken; SaveContract(strToken); bool bSaved = OTDB::StorePlainString(strToken.Get(), OTLog::SpentFolder(), strAssetFolder.Get(), strTokenHash.Get()); if (!bSaved) { OTLog::vError("OTToken::RecordTokenAsSpent: Error saving file: %s%s%s%s%s\n", OTLog::SpentFolder(), OTLog::PathSeparator(), strAssetFolder.Get(), OTLog::PathSeparator(), strTokenHash.Get()); } return bSaved; }
// So we can implement the SAMY hash, which is currently an XOR of SHA-256 with WHRLPOOL // // Originally, it was SHA512 and WHRLPOOL, which both have a 512-bit output-size. // I was then going to cut the result in half and XOR together again. But then I // though, for now, instead of doing all that extra work, I'll just change the // two "HashAlgorithms" from SHA512 and WHRLPOOL to SHA256 and WHIRLPOOL. // // This was very much easier, as I only had to change the little "512" to say // "256" instead, and basically the job was done. Of course, this means that OT // is generating a 256-bit hash in THIS object, and a 512-bit WHIRLPOOL hash in // the other object. i.e. There is still one 512-bit hash that you are forced to // calculate, even if you throw away half of it after the calculation is done. // // Since the main object has a 256-bit hash, the XOR() function below was already // coded to XOR the minimum length based on the smallest of the two objects. // Therefore, it will XOR 256 bits of the WHRLPOOL output into the 256 bits of // the main output (SHA256) and stop there: we now have a 256 bit ID. // // The purpose here is to reduce the ID size so that it will work on Windows with // the filenames. The current 512bit output is 64 bytes, or 128 characters when // exported to a hex string (in all the OT contracts for example, over and over // again.) // // The new size will be 256bit, which is 32 bytes of binary. In hex string that // would be 64 characters. But we're also converting from Hex to Base62, which // means we'll get it down to around 43 characters. // // This means our IDs are going to go from this: // // // To this: // // // You might ask: is 256 bits big enough of a hash size to be safe from collisions? // Practically speaking, I believe so. The IDs are used for accounts, servers, asset types, // and users. How many different asset types do you expect there will be, where changing the // contract to anything still intelligible would result in a still-valid signature? To find // a collision in a contract, where the signature would still work, would you expect the necessary // changed plaintext to be something that would still make sense in the contract? Would such // a random piece of data turn out to form proper XML? // // 256bits is enough to store the number of atoms in the Universe. If we ever need a bigger // hashsize, just go change the HashAlgorithm1 back to "SHA512" instead of "SHA256", and you'll // instantly have a doubled hash output size :-) // bool OTIdentifier::XOR(const OTIdentifier & theInput) { // Go with the smallest of the two const int64_t lSize = (GetSize() > theInput.GetSize() ? theInput.GetSize() : GetSize()); for (int32_t i = 0; i < lSize; i++) { // When converting to BigInteger internally, this will be a bit more efficient. ((char*)GetPointer())[i] ^= ((char*)theInput.GetPointer())[i]; // todo cast } return true; }
bool OTPayment::GetSenderAcctID(OTIdentifier & theOutput) const { theOutput.Release(); // ---------------------- if (!m_bAreTempValuesSet) return false; bool bSuccess = false; switch (m_Type) { case OTPayment::CHEQUE: case OTPayment::VOUCHER: case OTPayment::INVOICE: case OTPayment::PAYMENT_PLAN: theOutput = m_SenderAcctID; bSuccess = true; break; case OTPayment::SMART_CONTRACT: case OTPayment::PURSE: bSuccess = false; break; default: OTLog::Error("OTPayment::GetSenderAcctID: Bad payment type!\n"); break; } return bSuccess; }
bool OTAccount::GetOutboxHash(OTIdentifier & theOutput) { theOutput.Release(); if (!m_OutboxHash.IsEmpty()) { theOutput = m_OutboxHash; return true; } else if (!GetUserID().IsEmpty() && !GetRealAccountID().IsEmpty() && !GetRealServerID().IsEmpty() ) { OTLedger theOutbox(GetUserID(), GetRealAccountID(), GetRealServerID()); if (theOutbox.LoadOutbox() && theOutbox.CalculateOutboxHash(theOutput)) { SetOutboxHash(theOutput); return true; } } return false; }
bool OTPurse::GetNymID(OTIdentifier & theOutput) const { bool bSuccess = false; theOutput.Release(); // -------------------------------------- if (this->IsNymIDIncluded() && !m_UserID.IsEmpty()) { bSuccess = true; theOutput = m_UserID; } // -------------------------------------- else if (this->IsUsingATempNym() && (NULL != m_pTempNym)) { bSuccess = true; m_pTempNym->GetIdentifier(theOutput); } // -------------------------------------- else if (!m_UserID.IsEmpty()) { bSuccess = true; theOutput = m_UserID; } // -------------------------------------- return bSuccess; }
// Note: ALL failures will return true, even if the token has NOT already been // spent, and the failure was actually due to a directory creation error. Why, // you might ask? Because no matter WHAT is causing the failure, any return of // false is a signal that the token is SAFE TO ACCEPT AS TENDER. If there was a // temporary file system error, someone could suddenly deposit the same token // over and over again and this method would return "false" (Token is "not already // spent.") // // We simply cannot risk that, so false is not returned unless execution reaches // the very bottom of this method. Every other error acts as if the token is // no good, for security reasons. If the token really IS good, the user can submit // it again later and it will work. // bool OTToken::IsTokenAlreadySpent(OTString & theCleartextToken) { OTString strAssetID(GetAssetID()); // ---------------------------------------------------------------------------- // Calculate the filename (a hash of the Lucre cleartext token ID) OTIdentifier theTokenHash; theTokenHash.CalculateDigest(theCleartextToken); // Grab the new hash into a string (for use as a filename) OTString strTokenHash(theTokenHash); // ---------------------------------------------------------------------------- OTString strAssetFolder; strAssetFolder.Format("%s.%d", strAssetID.Get(), GetSeries()); bool bTokenIsPresent = OTDB::Exists(OTLog::SpentFolder(), strAssetFolder.Get(), strTokenHash.Get()); // -------------------------------------------------------------------- if (bTokenIsPresent) { OTLog::vOutput(0, "\nOTToken::IsTokenAlreadySpent: Token was already spent: %s%s%s%s%s\n", OTLog::SpentFolder(), OTLog::PathSeparator(), strAssetFolder.Get(), OTLog::PathSeparator(), strTokenHash.Get()); return true; // all errors must return true in this function. // But this is not an error. Token really WAS already } // spent, and this true is for real. The others are just // for security reasons because of this one. // This is the ideal case: the token was NOT already spent, it was good, // so we can return false and the depositor can be credited appropriately. // IsTokenAlreadySpent? NO-it was NOT already spent. You can only POSSIBLY // get a false out of this method if you actually reached the bottom (here.) return false; }
// This method implements the SAMY hash bool OTIdentifier::CalculateDigest(const OTData & dataInput) { //#ifndef ANDROID // SHA256 on Android; no whirlpool until OpenSSL 1.0.0 is added. OTIdentifier idSecondHash; if (idSecondHash.CalculateDigest(dataInput, HashAlgorithm2) && CalculateDigest(dataInput, HashAlgorithm1)) { // At this point, we have successfully generated the WHRLPOOL hash in // idSecondHash, and we've successfully generated the SHA-256 hash in // this object. // Next we XOR them together for the final product. return XOR(idSecondHash); } //#else // ANDROID // if (CalculateDigest(dataInput, HashAlgorithm1)) // SHA256 only until I add the new OpenSSL 1.0 for Android // { // return true; // } //#endif // ANDROID return false; }
// This method implements the SAMY hash bool OTIdentifier::CalculateDigest(const OTString & strInput) { #ifndef ANDROID // If NOT Android... OTIdentifier idSecondHash; if (idSecondHash.CalculateDigest(strInput, HashAlgorithm2) && CalculateDigest(strInput, HashAlgorithm1)) { // At this point, we have successfully generated the WHRLPOOL hash in // idSecondHash, and we've successfully generated the SHA-256 hash in // this object. // Next we XOR them together for the final product. return XOR(idSecondHash); } #else // SHA256 on Android; no whirlpool until OpenSSL 1.0.0 is added. if (CalculateDigest(strInput, HashAlgorithm1)) { return true; } #endif // ANDROID return false; }
// Most contracts calculate their ID by hashing the Raw File (signatures and all). // The Basket only hashes the unsigned contents, and only with the account IDs removed. // This way, the basket will produce a consistent ID across multiple different servers. void OTBasket::CalculateContractID(OTIdentifier & newID) { const OTString strContents(m_xmlUnsigned); // Produce a version of the file without account IDs (which are different from server to server.) // m_bHideAccountID = true; UpdateContents(); // <========= newID.CalculateDigest(m_xmlUnsigned); // Put it back the way it was. m_bHideAccountID = false; // UpdateContents(); // No need to do this, we already had this string before (above). m_xmlUnsigned = strContents; // Here we just set it back again. }
bool OTPayment::GetRecipientAcctID(OTIdentifier & theOutput) const { // NOTE: // A cheque HAS NO "Recipient Asset Acct ID", since the recipient's account (where he deposits // the cheque) is not known UNTIL the time of the deposit. It's certain not known at the time // that the cheque is written... theOutput.Release(); // ---------------------- if (!m_bAreTempValuesSet) return false; bool bSuccess = false; switch (m_Type) { case OTPayment::PAYMENT_PLAN: if (m_bHasRecipient) { theOutput = m_RecipientAcctID; bSuccess = true; } else bSuccess = false; break; case OTPayment::CHEQUE: case OTPayment::VOUCHER: case OTPayment::INVOICE: case OTPayment::SMART_CONTRACT: case OTPayment::PURSE: // A purse might have a recipient USER, but never a recipient ACCOUNT. bSuccess = false; break; default: OTLog::Error("OTPayment::GetRecipientAcctID: Bad payment type!\n"); break; } return bSuccess; }
// With a voucher (cashier's cheque) the "bank" is the "sender", // whereas the actual Nym who purchased it is the "remitter." // bool OTPayment::GetRemitterUserID(OTIdentifier & theOutput) const { theOutput.Release(); // ---------------------- if (!m_bAreTempValuesSet) return false; bool bSuccess = false; switch (m_Type) { case OTPayment::VOUCHER: theOutput = m_RemitterUserID; bSuccess = true; break; default: OTLog::Error("OTPayment::GetRemitterUserID: Bad payment type! Expected a voucher cheque.\n"); break; } return bSuccess; }
bool operator<(const OTIdentifier& lhs, const OTIdentifier& rhs) { return lhs.get() < rhs.get(); }
int main (int argc, char **argv) { OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Client -- version %s\n" "(transport build: OTMessage -> TCP -> SSL)\n" "IF YOU PREFER TO USE ZMQ (message based), then rebuild from main folder like this:\n" "cd ..; make clean; make\n\n", OTLog::Version()); OT_API::InitOTAPI(); // ----------------------------------------------------------------------- // The beginnings of an INI file!! OTString strPath; { CSimpleIniA ini; // We're assuming this file is on the path. SI_Error rc = ini.LoadFile("./.ot_ini"); // todo: stop hardcoding. if (rc >=0) { const char * pVal = ini.GetValue("paths", "client_path", SERVER_PATH_DEFAULT); // todo stop hardcoding. if (NULL != pVal) strPath.Set(pVal); else strPath.Set(SERVER_PATH_DEFAULT); } else { strPath.Set(SERVER_PATH_DEFAULT); } } // ----------------------------------------------------------------------- OTString strCAFile, strKeyFile, strSSLPassword; if (argc < 2) { OTLog::vOutput(0, "\n==> USAGE: %s <SSL-password> <absolute_path_to_data_folder>\n\n" #if defined (FELLOW_TRAVELER) "(Password defaults to '%s' if left blank.)\n" "(Folder defaults to '%s' if left blank.)\n" #else "(The test password is always 'test'.\n'cd data_folder' then 'pwd' to see the absolute path.)\n" #endif "\n\n", argv[0] #if defined (FELLOW_TRAVELER) , KEY_PASSWORD, strPath.Get() #endif ); #if defined (FELLOW_TRAVELER) strSSLPassword.Set(KEY_PASSWORD); OTString strClientPath(strPath.Get()); g_OT_API.Init(strClientPath); // SSL gets initialized in here, before any keys are loaded. #else exit(1); #endif } else if (argc < 3) { OTLog::vOutput(0, "\n==> USAGE: %s <SSL-password> <absolute_path_to_data_folder>\n\n" #if defined (FELLOW_TRAVELER) "(Folder defaults to '%s' if left blank.)\n" #endif "\n\n", argv[0] #if defined (FELLOW_TRAVELER) , strPath.Get() #endif ); #if defined (FELLOW_TRAVELER) strSSLPassword.Set(argv[1]); OTString strClientPath(strPath.Get()); g_OT_API.Init(strClientPath); // SSL gets initialized in here, before any keys are loaded. #else exit(1); #endif } else { strSSLPassword.Set(argv[1]); OTString strClientPath(argv[2]); g_OT_API.Init(strClientPath); // SSL gets initialized in here, before any keys are loaded. } OTLog::vOutput::(0, "Using as path to data folder: %s\n", OTLog::Path()); strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE); strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE); // ------------------------------------------------------------------------------ // // Basically, loop: // // 1) Present a prompt, and get a user string of input. Wait for that. // // 2) Process it out as an OTMessage to the server. It goes down the pipe. // // 3) Sleep for 1 second. // // 4) Awake and check for messages to be read in response from the server. // Loop. As long as there are any responses there, then process and handle // them all. // Then continue back up to the prompt at step (1). char buf[200] = ""; int retVal = 0; int nExpectResponse = 0; OTLog::Output(0, "You may wish to 'load' then 'connect' then 'stat'.\n"); OTLog::vOutput(4, "Starting client loop. u_header size in C code is %d.\n", OT_CMD_HEADER_SIZE); for(;;) { buf[0] = 0; // Making it fresh again. nExpectResponse = 0; // 1) Present a prompt, and get a user string of input. Wait for that. OTLog::Output(0, "\nWallet> "); if (NULL == fgets(buf, sizeof(buf)-10, stdin)) // Leaving myself 10 extra bytes at the end for safety's sake. break; OTLog::Output(0, ".\n..\n...\n....\n.....\n......\n.......\n........\n.........\n..........\n" "...........\n............\n.............\n"); // so we can process the user input std::string strLine = buf; // 1.5 The one command that doesn't involve a message to the server (so far) // is the command to load the wallet from disk (which we do before we can // do anything else.) That and maybe the message to CONNECT to the server. // Load wallet.xml if (strLine.compare(0,4,"load") == 0) { OTLog::Output(0, "User has instructed to load wallet.xml...\n"); g_OT_API.GetWallet()->LoadWallet("wallet.xml"); // g_OT_API.GetWallet()->SaveWallet("NEWwallet.xml"); // todo remove this test code. continue; } else if (strLine.compare(0,5,"clear") == 0) { if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available. Try 'load'.\n"); continue; } g_pTemporaryNym->RemoveAllNumbers(); g_pTemporaryNym->SaveSignedNymfile(*g_pTemporaryNym); OTLog::Output(0, "Successfully removed all issued and transaction numbers. Saving nym...\n"); continue; } else if (strLine.compare(0,7,"payment") == 0) { if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available to sign the payment plan with. Try 'load'.\n"); continue; } OTLog::Output(0, "Enter your Asset Account ID that the payments will come from: "); OTString strTemp; strTemp.OTfgets(std::cin); const OTIdentifier ACCOUNT_ID(strTemp), USER_ID(*g_pTemporaryNym); OTAccount * pAccount = g_OT_API.GetWallet()->GetAccount(ACCOUNT_ID); if (NULL == pAccount) { OTLog::Output(0, "That account isn't loaded right now. Try 'load'.\n"); continue; } // To write a payment plan, like a cheque, we need to burn one of our transaction numbers. (Presumably // the wallet is also storing a couple of these, since they are needed to perform any transaction.) // // I don't have to contact the server to write a payment plan -- as long as I already have a transaction // number I can use to write it. Otherwise I'd have to ask the server to send me one first. OTString strServerID(pAccount->GetRealServerID()); long lTransactionNumber=0; if (false == g_pTemporaryNym->GetNextTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber)) { OTLog::Output(0, "Payment Plans are written offline, but you still need a transaction number\n" "(and you have none, currently.) Try using 'n' to request another transaction number.\n"); continue; } // ----------------------------------------------------------------------- OTString str_RECIPIENT_USER_ID, str_RECIPIENT_ACCT_ID, strConsideration; // Get the Recipient Nym ID OTLog::Output(0, "Enter the Recipient's User ID (NymID): "); str_RECIPIENT_USER_ID.OTfgets(std::cin); // THEN GET AN ACCOUNT ID in that same asset type OTLog::Output(0, "Enter the Recipient's ACCOUNT ID (of the same asset type as your account): "); str_RECIPIENT_ACCT_ID.OTfgets(std::cin); OTLog::Output(0, "Enter a memo describing consideration for the payment plan: "); strConsideration.OTfgets(std::cin); const OTIdentifier RECIPIENT_USER_ID(str_RECIPIENT_USER_ID), RECIPIENT_ACCT_ID(str_RECIPIENT_ACCT_ID); OTPaymentPlan thePlan(pAccount->GetRealServerID(), pAccount->GetAssetTypeID(), pAccount->GetRealAccountID(), pAccount->GetUserID(), RECIPIENT_ACCT_ID, RECIPIENT_USER_ID); // ----------------------------------------------------------------------- // Valid date range (in seconds) OTLog::Output(0, " 6 minutes == 360 Seconds\n" "10 minutes == 600 Seconds\n" "1 hour == 3600 Seconds\n" "1 day == 86400 Seconds\n" "30 days == 2592000 Seconds\n" "3 months == 7776000 Seconds\n" "6 months == 15552000 Seconds\n\n" ); long lExpirationInSeconds = 86400; OTLog::vOutput(0, "How many seconds before payment plan expires? (defaults to 1 day: %ld): ", lExpirationInSeconds); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 1) lExpirationInSeconds = atol(strTemp.Get()); // ----------------------------------------------------------------------- time_t VALID_FROM = time(NULL); // This time is set to TODAY NOW OTLog::vOutput(0, "Payment plan becomes valid for processing STARTING date\n" "(defaults to now, in seconds) [%ld]: ", VALID_FROM); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 2) VALID_FROM = atol(strTemp.Get()); const time_t VALID_TO = VALID_FROM + lExpirationInSeconds; // now + 86400 // ----------------------------------------------------------------------- bool bSuccessSetAgreement = thePlan.SetAgreement(lTransactionNumber, strConsideration, VALID_FROM, VALID_TO); if (!bSuccessSetAgreement) { OTLog::Output(0, "Failed trying to set the agreement!\n"); // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS. g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true continue; } bool bSuccessSetInitialPayment = true; // the default, in case user chooses not to even have this payment. bool bSuccessSetPaymentPlan = true; // the default, in case user chooses not to have a payment plan // ----------------------------------------------------------------------- OTLog::Output(0, "What is the Initial Payment Amount, if any? [0]: "); strTemp.Release(); strTemp.OTfgets(std::cin); long lInitialPayment = atol(strTemp.Get()); if (lInitialPayment > 0) { time_t PAYMENT_DELAY = 60; // 60 seconds. OTLog::vOutput(0, "From the Start Date forward, how long until the Initial Payment should charge?\n" "(defaults to one minute, in seconds) [%d]: ", PAYMENT_DELAY); strTemp.Release(); strTemp.OTfgets(std::cin); if ((strTemp.GetLength() > 1) && atol(strTemp.Get())>0) PAYMENT_DELAY = atol(strTemp.Get()); // ----------------------------------------------------------------------- bSuccessSetInitialPayment = thePlan.SetInitialPayment(lInitialPayment, PAYMENT_DELAY); } if (!bSuccessSetInitialPayment) { OTLog::Output(0, "Failed trying to set the initial payment!\n"); // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS. g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true continue; } // ----------------------------------------------------------------------- OTLog::Output(0, "What is the regular payment amount, if any? [0]: "); strTemp.Release(); strTemp.OTfgets(std::cin); long lRegularPayment = atol(strTemp.Get()); if (lRegularPayment > 0) // If there are regular payments. { // ----------------------------------------------------------------------- time_t PAYMENT_DELAY = 120; // 120 seconds. OTLog::vOutput(0, "From the Start Date forward, how long until the Regular Payments start?\n" "(defaults to two minutes, in seconds) [%d]: ", PAYMENT_DELAY); strTemp.Release(); strTemp.OTfgets(std::cin); if ((strTemp.GetLength() > 1) && atol(strTemp.Get())>0) PAYMENT_DELAY = atol(strTemp.Get()); // ----------------------------------------------------------------------- time_t PAYMENT_PERIOD = 30; // 30 seconds. OTLog::vOutput(0, "Once payments begin, how much time should elapse between each payment?\n" "(defaults to thirty seconds) [%d]: ", PAYMENT_PERIOD); strTemp.Release(); strTemp.OTfgets(std::cin); if ((strTemp.GetLength() > 1) && atol(strTemp.Get())>0) PAYMENT_PERIOD = atol(strTemp.Get()); // ----------------------------------------------------------------------- time_t PLAN_LENGTH = 0; // 0 seconds (for no max length). OTLog::vOutput(0, "From start date, do you want the plan to expire after a certain maximum time?\n" "(defaults to 0 for no) [%d]: ", PLAN_LENGTH); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 1) PLAN_LENGTH = atol(strTemp.Get()); // ----------------------------------------------------------------------- OTLog::Output(0, "Should there be some maximum number of payments? (Zero for no maximum.) [0]: "); strTemp.Release(); strTemp.OTfgets(std::cin); int nMaxPayments = atoi(strTemp.Get()); bSuccessSetPaymentPlan = thePlan.SetPaymentPlan(lRegularPayment, PAYMENT_DELAY, PAYMENT_PERIOD, PLAN_LENGTH, nMaxPayments); } if (!bSuccessSetPaymentPlan) { OTLog::Output(0, "Failed trying to set the payment plan!\n"); // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS. g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true continue; } thePlan.SignContract(*g_pTemporaryNym); thePlan.SaveContract(); OTString strPlan(thePlan); OTLog::vOutput(0, "\n\n(Make sure Both Parties sign the payment plan before submitting to server):\n\n\n%s\n", strPlan.Get()); continue; } else if (strLine.compare(0,6,"cheque") == 0) { if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available to sign the cheque with. Try 'load'.\n"); continue; } OTLog::Output(0, "Enter the ID for your Asset Account that the cheque will be drawn on: "); OTString strTemp; strTemp.OTfgets(std::cin); const OTIdentifier ACCOUNT_ID(strTemp), USER_ID(*g_pTemporaryNym); OTAccount * pAccount = g_OT_API.GetWallet()->GetAccount(ACCOUNT_ID); if (NULL == pAccount) { OTLog::Output(0, "That account isn't loaded right now. Try 'load'.\n"); continue; } // To write a cheque, we need to burn one of our transaction numbers. (Presumably the wallet // is also storing a couple of these, since they are needed to perform any transaction.) // // I don't have to contact the server to write a cheque -- as long as I already have a transaction // number I can use to write it. Otherwise I'd have to ask the server to send me one first. OTString strServerID(pAccount->GetRealServerID()); long lTransactionNumber=0; if (false == g_pTemporaryNym->GetNextTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber)) { OTLog::Output(0, "Cheques are written offline, but you still need a transaction number\n" "(and you have none, currently.) Try using 'n' to request another transaction number.\n"); continue; } OTCheque theCheque(pAccount->GetRealServerID(), pAccount->GetAssetTypeID()); // Recipient OTLog::Output(0, "Enter a User ID for the recipient of this cheque (defaults to blank): "); OTString strRecipientUserID; strRecipientUserID.OTfgets(std::cin); const OTIdentifier RECIPIENT_USER_ID(strRecipientUserID.Get()); // Amount OTLog::Output(0, "Enter an amount: "); strTemp.Release(); strTemp.OTfgets(std::cin); const long lAmount = atol(strTemp.Get()); // ----------------------------------------------------------------------- // Memo OTLog::Output(0, "Enter a memo for your check: "); OTString strChequeMemo; strChequeMemo.OTfgets(std::cin); // ----------------------------------------------------------------------- // Valid date range (in seconds) OTLog::Output(0, " 6 minutes == 360 Seconds\n" "10 minutes == 600 Seconds\n" "1 hour == 3600 Seconds\n" "1 day == 86400 Seconds\n" "30 days == 2592000 Seconds\n" "3 months == 7776000 Seconds\n" "6 months == 15552000 Seconds\n\n" ); long lExpirationInSeconds = 3600; OTLog::vOutput(0, "How many seconds before cheque expires? (defaults to 1 hour: %ld): ", lExpirationInSeconds); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 1) lExpirationInSeconds = atol(strTemp.Get()); // ----------------------------------------------------------------------- time_t VALID_FROM = time(NULL); // This time is set to TODAY NOW OTLog::vOutput(0, "Cheque may be cashed STARTING date (defaults to now, in seconds) [%ld]: ", VALID_FROM); strTemp.Release(); strTemp.OTfgets(std::cin); if (strTemp.GetLength() > 2) VALID_FROM = atol(strTemp.Get()); const time_t VALID_TO = VALID_FROM + lExpirationInSeconds; // now + 3600 // ----------------------------------------------------------------------- bool bIssueCheque = theCheque.IssueCheque(lAmount, lTransactionNumber, VALID_FROM, VALID_TO, ACCOUNT_ID, USER_ID, strChequeMemo, (strRecipientUserID.GetLength() > 2) ? &(RECIPIENT_USER_ID) : NULL); if (bIssueCheque) { theCheque.SignContract(*g_pTemporaryNym); theCheque.SaveContract(); OTString strCheque(theCheque); OTLog::vOutput(0, "\n\nOUTPUT:\n\n\n%s\n", strCheque.Get()); } else { OTLog::Output(0, "Failed trying to issue the cheque!\n"); // IF FAILED, ADD TRANSACTION NUMBER BACK TO LIST OF AVAILABLE NUMBERS. g_pTemporaryNym->AddTransactionNum(*g_pTemporaryNym, strServerID, lTransactionNumber, true); // bSave=true } continue; } else if (strLine.compare(0,7,"decrypt") == 0) { if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available to decrypt with.\n"); continue; } OTLog::Output(0, "Enter text to be decrypted:\n> "); OTASCIIArmor theArmoredText; char decode_buffer[200]; // Safe since we only read sizeof - 1 do { decode_buffer[0] = 0; if (NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) { theArmoredText.Concatenate("%s\n", decode_buffer); OTLog::Output(0, "> "); } else { break; } } while (strlen(decode_buffer)>1); OTEnvelope theEnvelope(theArmoredText); OTString strDecodedText; theEnvelope.Open(*g_pTemporaryNym, strDecodedText); OTLog::vOutput(0, "\n\nDECRYPTED TEXT:\n\n%s\n\n", strDecodedText.Get()); continue; } else if (strLine.compare(0,6,"decode") == 0) { OTLog::Output(0, "Enter text to be decoded:\n> "); OTASCIIArmor theArmoredText; char decode_buffer[200]; // Safe since we only read sizeof - 1. do { decode_buffer[0] = 0; if (NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) { theArmoredText.Concatenate("%s\n", decode_buffer); OTLog::Output(0, "> "); } else { break; } } while (strlen(decode_buffer)>1); OTString strDecodedText(theArmoredText); OTLog::vOutput(0, "\n\nDECODED TEXT:\n\n%s\n\n", strDecodedText.Get()); continue; } else if (strLine.compare(0,6,"encode") == 0) { OTLog::Output(0, "Enter text to be ascii-encoded (terminate with ~ on a new line):\n> "); OTString strDecodedText; char decode_buffer[200]; // Safe since we only read sizeof - 1. do { decode_buffer[0] = 0; if ((NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) && (decode_buffer[0] != '~')) { strDecodedText.Concatenate("%s", decode_buffer); OTLog::Output(0, "> "); } else { break; } } while (decode_buffer[0] != '~'); OTASCIIArmor theArmoredText(strDecodedText); OTLog::vOutput(0, "\n\nENCODED TEXT:\n\n%s\n\n", theArmoredText.Get()); continue; } else if (strLine.compare(0,4,"hash") == 0) { OTLog::Output(0, "Enter text to be hashed (terminate with ~ on a new line):\n> "); OTString strDecodedText; char decode_buffer[200]; // Safe since we only read sizeof - 1. do { decode_buffer[0] = 0; if ((NULL != fgets(decode_buffer, sizeof(decode_buffer)-1, stdin)) && (decode_buffer[0] != '~')) { strDecodedText.Concatenate("%s\n", decode_buffer); OTLog::Output(0, "> "); } else { break; } } while (decode_buffer[0] != '~'); OTIdentifier theIdentifier; theIdentifier.CalculateDigest(strDecodedText); OTString strHash(theIdentifier); OTLog::vOutput(0, "\n\nMESSAGE DIGEST:\n\n%s\n\n", strHash.Get()); continue; } else if (strLine.compare(0,4,"stat") == 0) { OTLog::Output(0, "User has instructed to display wallet contents...\n"); OTString strStat; g_OT_API.GetWallet()->DisplayStatistics(strStat); OTLog::vOutput(0, "%s\n", strStat.Get()); continue; } else if (strLine.compare(0,4,"help") == 0) { OTLog::Output(0, "User has instructed to display the help file...\n"); system("more ../docs/CLIENT-COMMANDS.txt"); continue; } else if (strLine.compare(0,4,"quit") == 0) { OTLog::Output(0, "User has instructed to exit the wallet...\n"); break; } // 1.6 Connect to the first server in the wallet. (assuming it loaded a server contract.) else if (strLine.compare(0,7,"connect") == 0) { OTLog::Output(0, "User has instructed to connect to the first server available in the wallet.\n"); if (NULL == g_pTemporaryNym) { OTLog::Output(0, "No Nym yet available to connect with. Try 'load'.\n"); continue; } // Wallet, after loading, should contain a list of server // contracts. Let's pull the hostname and port out of // the first contract, and connect to that server. bool bConnected = g_OT_API.GetClient()->ConnectToTheFirstServerOnList(*g_pTemporaryNym, strCAFile, strKeyFile, strSSLPassword); if (bConnected) OTLog::Output(0, "Success. (Connected to the first notary server on your wallet's list.)\n"); else { OTLog::Output(0, "Either the wallet is not loaded, or there was an error connecting to server.\n"); } continue; } if (!g_OT_API.GetClient()->IsConnected()) { OTLog::Output(0, "(You are not connected to a notary server--you cannot send commands.)\n"); continue; } // 2) Process it out as an OTMessage to the server. It goes down the pipe. g_OT_API.GetClient()->ProcessMessageOut(buf, &nExpectResponse); // 3) Sleep for 1 second. #ifdef _WIN32 OT_Sleep(1000); #else sleep (1); #endif bool bFoundMessage = false; // 4) While there are messages to be read in response from the server, // then process and handle them all. do { OTMessage * pMsg = new OTMessage; OT_ASSERT(NULL != pMsg); // If this returns true, that means a Message was // received and processed into an OTMessage object (theMsg) bFoundMessage = g_OT_API.GetClient()->ProcessInBuffer(*pMsg); if (true == bFoundMessage) { // OTString strReply; // theMsg.SaveContract(strReply); // OTLog::vOutput(0, "\n\n**********************************************\n" // "Successfully in-processed server response.\n\n%s\n", strReply.Get()); g_OT_API.GetClient()->ProcessServerReply(*pMsg); // the Client takes ownership and will handle cleanup. } else { delete pMsg; pMsg = NULL; } } while (true == bFoundMessage); } // for OTLog::Output(0, "Finished running client.\n"); #ifdef _WIN32 WSACleanup(); #endif return retVal; }
bool operator!=(const OTIdentifier& lhs, const Identifier& rhs) { return lhs.get() != rhs; }
_SharedPtr<OTAccount> OTAcctList::GetOrCreateAccount(OTPseudonym & theServerNym, const OTIdentifier & ACCOUNT_OWNER_ID, const OTIdentifier & ASSET_TYPE_ID, const OTIdentifier & SERVER_ID, bool & bWasAcctCreated, // this will be set to true if the acct is created here. Otherwise set to false; const int64_t lStashTransNum/*=0*/) { _SharedPtr<OTAccount> pRetVal; bWasAcctCreated = false; // ------------------------------------------------ if (OTAccount::stash == m_AcctType) { if (lStashTransNum <= 0) { OTLog::Error("OTAcctList::GetOrCreateAccount: Failed attempt to create stash account without cron item #.\n"); return pRetVal; } } // ------------------------------------------------ // First, we'll see if there's already an account ID available for the requested asset type ID. // const OTString strAssetTypeID(ASSET_TYPE_ID); const std::string str_asset_type_id = strAssetTypeID.Get(); // ------------------------------------ OTString strAcctType; TranslateAccountTypeToString(m_AcctType, strAcctType); // ---------------------------------------------------------------- mapOfStrings::iterator it_acct_ids = m_mapAcctIDs.find(str_asset_type_id); if (m_mapAcctIDs.end() != it_acct_ids) // Account ID *IS* already there for this asset type... { const std::string str_account_id = (*it_acct_ids).second; // grab account ID mapOfWeakAccounts::iterator it_weak = m_mapWeakAccts.find(str_account_id); // Try to find account pointer... if (m_mapWeakAccts.end() != it_weak) // FOUND the weak ptr to the account! Maybe it's already loaded { // bool bSuccess = true; _WeakPtr<OTAccount> pWeak = (*it_weak).second; // first is acct ID, second is weak_ptr to account. try { _SharedPtr<OTAccount> pShared(pWeak); // If success, then we have a shared pointer. But it's worrying (TODO) because this should have // gone out of scope and been destroyed by whoever ELSE was using it. The fact that it's still here... // well I'm glad not to double-load it, but I wonder why it's still here? And we aren't walking on anyone's // toes, right? If this were multi-threaded, then I'd explicitly lock a mutex here, honestly. But since things // happen one at a time on OT, I'll settle for a warning for now. I'm assuming that if the account's loaded // already somewhere, it's just a pointer sitting there, and we're not walking on each other's toes. // if (pShared) { OTLog::vOutput(0, "OTAcctList::GetOrCreateAccount: Warning: account (%s) was already in memory so I gave you a " "pointer to the existing one. (But who else has a copy of it?) \n", str_account_id.c_str()); return pShared; } } catch (...) { } // Though the weak pointer was there, the resource must have since been destroyed, // because I cannot lock a new shared ptr onto it. :-( // // Therefore remove it from the map, and RE-LOAD IT. // m_mapWeakAccts.erase(it_weak); } // DIDN'T find the acct pointer, even though we had the ID. // (Or it was there, but we couldn't lock a shared_ptr onto it, so we erased it...) // // So let's load it now. After all, the Account ID *does* exist... // const OTString strAcctID(str_account_id.c_str()); const OTIdentifier theAccountID(strAcctID); // The Account ID exists, but we don't have the pointer to a loaded account for it. // Soo.... let's load it. // OTAccount * pAccount = OTAccount::LoadExistingAccount(theAccountID, SERVER_ID); if (NULL == pAccount) OTLog::vError("OTAcctList::GetOrCreateAccount: Failed trying to load %s account with account ID: %s\n", strAcctType.Get(),strAcctID.Get()); else if (!pAccount->VerifySignature(theServerNym)) OTLog::vError("OTAcctList::GetOrCreateAccount: Failed verifying server's signature on %s account with account ID: %s\n", strAcctType.Get(),strAcctID.Get()); else if (!pAccount->VerifyOwnerByID(ACCOUNT_OWNER_ID)) { const OTString strOwnerID(ACCOUNT_OWNER_ID); OTLog::vError("OTAcctList::GetOrCreateAccount: Failed verifying owner ID (%s) on %s account ID: %s\n", strOwnerID.Get(), strAcctType.Get(),strAcctID.Get()); } else // SUCCESS loading the account... { OTLog::vOutput(3, "Successfully loaded %s account ID: %s Asset Type ID: %s\n", strAcctType.Get(), strAcctID.Get(), str_asset_type_id.c_str()); pRetVal = _SharedPtr<OTAccount>(pAccount); // Create a shared pointer to the account, so it will be cleaned up automatically. m_mapWeakAccts [strAcctID.Get()] = _WeakPtr<OTAccount>(pRetVal); // save a weak pointer to the acct, so we'll never load it twice, but we'll also know if it's been deleted. } return pRetVal; // } // (Asset Type ID was found on the AcctID Map -- a corresponding Account ID is already there for that asset type.) // ****************************************************************************** // Not found... There's no account ID yet for that asset type ID. // That means we can create it... // OTMessage theMessage; // Here we set up theMessage ACCOUNT_OWNER_ID.GetString(theMessage.m_strNymID); ASSET_TYPE_ID.GetString(theMessage.m_strAssetID); SERVER_ID.GetString(theMessage.m_strServerID); OTAccount * pAccount = OTAccount::GenerateNewAccount(ACCOUNT_OWNER_ID, // theUserID SERVER_ID, // theServerID theServerNym, // theServerNym theMessage, m_AcctType, // OTAccount::voucher is default. lStashTransNum); // ------------------------------------------------------------------------------------------ if (NULL == pAccount) OTLog::vError(" OTAcctList::GetOrCreateAccount: Failed trying to generate %s account with asset type ID: %s\n", strAcctType.Get(), str_asset_type_id.c_str()); else // SUCCESS creating the account... { OTString strAcctID; pAccount->GetIdentifier(strAcctID); OTLog::vOutput(0, "Successfully created %s account ID: %s Asset Type ID: %s\n", strAcctType.Get(), strAcctID.Get(), str_asset_type_id.c_str()); pRetVal = _SharedPtr<OTAccount>(pAccount); // Create a shared pointer to the account, so it will be cleaned up automatically. m_mapWeakAccts [strAcctID.Get()] = _WeakPtr<OTAccount>(pRetVal); // save a weak pointer to the acct, so we'll never load it twice, but we'll also know if it's been deleted. m_mapAcctIDs [theMessage.m_strAssetID.Get()] = strAcctID.Get(); // Save the new acct ID in a map, keyed by asset type ID. bWasAcctCreated = true; } return pRetVal; }
// The above method uses this one internally... bool OTAccount::GenerateNewAccount(const OTPseudonym & theServer, const OTMessage & theMessage, const OTAccount::AccountType eAcctType/*=OTAccount::simple*/) { // First we generate a secure random number into a binary object. OTPayload thePayload; thePayload.SetPayloadSize(100); if (!RAND_bytes((unsigned char*)thePayload.GetPayloadPointer(), 100)) { OTLog::Error("The PRNG is not seeded!\n"); // abort( ); return false; } // Next we calculate that binary object into a message digest (an OTIdentifier). OTIdentifier newID; if (!newID.CalculateDigest(thePayload)) { OTLog::Error("Error generating new account ID.\n"); return false; } // Next we get that digest (which is a binary hash number) // and extract a human-readable standard string format of that hash, // into an OTString. OTString strID(newID); SetRealAccountID(newID); // Set the account number based on what we just generated. SetPurportedAccountID(newID); // Might as well set them both. (Safe here to do so, for once.) m_strName.Set(strID); // So it's not blank. The user can always change it. // Next we create the full path filename for the account using the ID. m_strFilename.Format("%s%s%s%s%s", OTLog::Path(), OTLog::PathSeparator(), OTLog::AccountFolder(), OTLog::PathSeparator(), strID.Get()); // Then we try to load it, in order to make sure that it doesn't already exist. if (LoadContractRawFile()) { OTLog::Error("Error generating new account ID, account already exists.\n"); return false; } // Set up the various important starting values of the account. m_AcctType = eAcctType; // account type defaults to OTAccount::simple. But there are also issuer accts... // for basket accounts, the server is the user. if (OTAccount::basket == eAcctType) { theServer.GetIdentifier(m_AcctUserID); } else { m_AcctUserID.SetString(theMessage.m_strNymID); } m_AcctAssetTypeID.SetString(theMessage.m_strAssetID); OTString TEMPstr(m_AcctAssetTypeID); OTLog::vOutput(3, "Creating new account, type:\n%s\nChanged to ID then back to string:\n%s\n", theMessage.m_strAssetID.Get(), TEMPstr.Get()); OTIdentifier SERVER_ID(theMessage.m_strServerID); SetRealServerID(SERVER_ID); // todo this assumes the serverID on the message is correct. It's vetted, but still... SetPurportedServerID(SERVER_ID); const time_t tDate = time(NULL); // Today, now. m_BalanceDate.Format("%d", tDate); m_BalanceAmount.Set("0"); // Sign the Account (so we know that we did)... Otherwise someone could put a fake // account file on the server if the code wasn't designed to verify the signature on the // account. SignContract(theServer); SaveContract(); // Save the Account to storage (based on its ID.) SaveAccount(); // Don't know why I had this here. Putting SaveAccount() instead. // OTString strFilename(m_strFilename); // SaveContract(strFilename.Get()); // Saves the account to a specific filename // No need to create the inbox and outbox ledgers...they will be created automatically // if they do not exist when they are needed. return true; }
// The above method uses this one internally... bool OTAccount::GenerateNewAccount(const OTPseudonym & theServer, const OTMessage & theMessage, const OTAccount::AccountType eAcctType/*=OTAccount::simple*/, int64_t lStashTransNum/*=0*/) { const char *szFunc = "OTAccount::GenerateNewAccount"; // ----------------------------------------------- // First we generate a secure random number into a binary object... // OTPayload thePayload; if (false == thePayload.Randomize(100)) // todo hardcoding. Plus: is 100 bytes of random a little much here? { OTLog::vError("%s: Failed trying to acquire random numbers.\n", szFunc); return false; } // -------------------------------------------------- // // Next we calculate that binary object into a message digest (an OTIdentifier). // OTIdentifier newID; if (!newID.CalculateDigest(thePayload)) { OTLog::vError("%s: Error generating new account ID.\n", szFunc); return false; } // -------------------------------------------------- // // Next we get that digest (which is a binary hash number) // and extract a human-readable standard string format of that hash, // into an OTString. // OTString strID(newID); SetRealAccountID(newID); // Set the account number based on what we just generated. SetPurportedAccountID(newID); // Might as well set them both. (Safe here to do so, for once.) m_strName.Set(strID); // So it's not blank. The user can always change it. // Next we create the full path filename for the account using the ID. // m_strFoldername = OTFolders::Account().Get(); m_strFilename = strID.Get(); // Then we try to load it, in order to make sure that it doesn't already exist. // -------------------------------------------------------------------- if (OTDB::Exists(m_strFoldername.Get(), m_strFilename.Get())) { OTLog::vError("%s: Account already exists: %s\n", szFunc, m_strFilename.Get()); return false; } // -------------------------------------------------------------------- // Set up the various important starting values of the account. m_AcctType = eAcctType; // account type defaults to OTAccount::simple. But there are also issuer accts... // -------------------------------------------------------------------- if (IsInternalServerAcct()) // basket, basketsub, mint, voucher, and stash accounts are all "owned" by the server. { theServer.GetIdentifier(m_AcctUserID); } else { m_AcctUserID.SetString(theMessage.m_strNymID); } // -------------------------------------------------------------------- m_AcctAssetTypeID.SetString(theMessage.m_strAssetID); OTLog::vOutput(3, "%s: Creating new account, type:\n%s\n", szFunc, theMessage.m_strAssetID.Get()); OTIdentifier SERVER_ID(theMessage.m_strServerID); SetRealServerID(SERVER_ID); // todo this assumes the serverID on the message is correct. It's vetted, but still... SetPurportedServerID(SERVER_ID); const time64_t tDate = OTTimeGetCurrentTime(); // Today, now. m_BalanceDate.Format("%d", tDate); m_BalanceAmount.Set("0"); // -------------------------------------------------------------------- if (IsStashAcct()) { OT_ASSERT_MSG(lStashTransNum > 0, "You created a stash account, but with a zero-or-negative transaction number for its cron item."); m_lStashTransNum = lStashTransNum; } // -------------------------------------------------------------------- // Sign the Account (so we know that we did)... Otherwise someone could put a fake // account file on the server if the code wasn't designed to verify the signature on the // account. SignContract(theServer); SaveContract(); // Save the Account to storage (based on its ID.) SaveAccount(); // Don't know why I had this here. Putting SaveAccount() instead. // OTString strFilename(m_strFilename); // SaveContract(strFilename.Get()); // Saves the account to a specific filename // No need to create the inbox and outbox ledgers...they will be created automatically // if they do not exist when they are needed. return true; }