// You usually wouldn't want to use this, since if the transaction failed, the opening number // is already burned and gone. But there might be cases where it's not, and you want to retrieve it. // So I added this function. // void OTAgreement::HarvestOpeningNumber(OTPseudonym & theNym) { // since we overrode the parent, we give it a chance to harvest also. // IF theNym is the original sender, the opening number will be harvested // inside this call. // OTCronItem::HarvestOpeningNumber(theNym); // The Nym is the original recipient. (If Compares true). // IN CASES where GetTransactionNum() isn't already burned, we can harvest it here. // if (theNym.CompareID(GetRecipientUserID())) { // This function will only "add it back" if it was really there in the first place. // (Verifies it is on issued list first, before adding to available list.) // theNym.ClawbackTransactionNumber(GetServerID(), GetRecipientOpeningNum(), true); //bSave=true } // NOTE: if the message failed (transaction never actually ran) then the sender AND recipient // can both reclaim their opening numbers. But if the message SUCCEEDED and the transaction FAILED, // then only the recipient can claim his opening number -- the sender's is already burned. So then, // what if you mistakenly call this function and pass the sender, when that number is already burned? // There's nothing this function can do, because we have no way of telling, from inside here, // whether the message succeeded or not, and whether the transaction succeeded or not. Therefore, // ==> we MUST rely on the CALLER to know this, and to avoid calling this function in the first place, // if he's sitting on a sender with a failed transaction. }
// Used for adding transaction numbers back to a Nym, after deciding not to use this agreement // or failing in trying to use it. Client side. // void OTAgreement::HarvestClosingNumbers(OTPseudonym & theNym) { // since we overrode the parent, we give it a chance to harvest also. // OTCronItem::HarvestClosingNumbers(theNym); // The Nym is the original recipient. (If Compares true). // FYI, if Nym is the original sender, then the above call will handle him. // // GetTransactionNum() is burned, but we can harvest the closing // numbers from the "Closing" list, which is only for the sender's numbers. // Subclasses will have to override this function for recipients, etc. // if (theNym.CompareID(GetRecipientUserID())) { const OTString strServerID(GetServerID()); for (int i = 0; i < GetRecipientCountClosingNumbers(); i++) { if (theNym.VerifyIssuedNum(strServerID, GetRecipientClosingTransactionNoAt(i))) // we only "add it back" if it was really there in the first place. theNym.AddTransactionNum(theNym, strServerID, GetRecipientClosingTransactionNoAt(i), (i == (GetRecipientCountClosingNumbers()-1) ? true : false)); // bSave=true only on the last iteration. } } }
/// See if theNym has rights to remove this item from Cron. /// bool OTAgreement::CanRemoveItemFromCron(OTPseudonym & theNym) { // You don't just go willy-nilly and remove a cron item from a market unless you check first // and make sure the Nym who requested it actually has said number (or a related closing number) // signed out to him on his last receipt... // if (true == OTCronItem::CanRemoveItemFromCron(theNym)) return true; const OTString strServerID(GetServerID()); // Usually the Nym is the originator. (Meaning GetTransactionNum() on this agreement // is still verifiable as an issued number on theNum, and belongs to him.) In that case, // the above call will discover this, and return true. // In other cases, theNym has the right to Remove the item even though theNym didn't originate it. // (Like if he is the recipient -- not the sender -- in a payment plan.) We check such things // HERE in this function (see below.) // if (false == theNym.CompareID(GetRecipientUserID())) { OTLog::Output(0, "OTAgreement::CanRemoveItemFromCron Weird: Nym tried to remove agreement (payment plan), even " "though he apparently wasn't the sender OR recipient.\n"); return false; } else if (this->GetRecipientCountClosingNumbers() < 2) { OTLog::vOutput(0, "OTAgreement::CanRemoveItemFromCron Weird: Recipient tried to remove agreement " "(or payment plan); expected 2 closing numbers to be available--that weren't. (Found %d).\n", this->GetRecipientCountClosingNumbers()); return false; } if (false == theNym.VerifyIssuedNum(strServerID, this->GetRecipientClosingNum())) { OTLog::Output(0, "OTAgreement::CanRemoveItemFromCron: Recipient Closing number didn't verify (for removal from cron).\n"); return false; } // By this point, we KNOW theNym is the sender, and we KNOW there are the proper number of transaction // numbers available to close. We also know that this cron item really was on the cron object, since // that is where it was looked up from, when this function got called! So I'm pretty sure, at this point, // to authorize removal, as long as the transaction num is still issued to theNym (this check here.) // return theNym.VerifyIssuedNum(strServerID, this->GetRecipientOpeningNum()); // Normally this will be all we need to check. The originator will have the transaction // number signed-out to him still, if he is trying to close it. BUT--in some cases, someone // who is NOT the originator can cancel. Like in a payment plan, the sender is also the depositor, // who would normally be the person cancelling the plan. But technically, the RECIPIENT should // also have the ability to cancel that payment plan. BUT: the transaction number isn't signed // out to the RECIPIENT... In THAT case, the below VerifyIssuedNum() won't work! In those cases, // expect that the special code will be in the subclasses override of this function. (OTPaymentPlan::CanRemoveItem() etc) // P.S. If you override this function, MAKE SURE to call the parent (OTCronItem::CanRemoveItem) first, // for the VerifyIssuedNum call above. Only if that fails, do you need to dig deeper... }
// You usually wouldn't want to use this, since if the transaction failed, the opening number // is already burned and gone. But there might be cases where it's not, and you want to retrieve it. // So I added this function. // void OTAgreement::HarvestOpeningNumber(OTPseudonym & theNym) { // since we overrode the parent, we give it a chance to harvest also. // OTCronItem::HarvestOpeningNumber(theNym); // The Nym is the original sender. (If Compares true). // IN CASES where GetTransactionNum() isn't already burned, we can harvest it here. // Subclasses will have to override this function for recipients, etc. // if (theNym.CompareID(GetRecipientUserID())) { const OTString strServerID(GetServerID()); if (theNym.VerifyIssuedNum(strServerID, GetRecipientOpeningNum())) // we only "add it back" if it was really there in the first place. theNym.AddTransactionNum(theNym, strServerID, GetRecipientOpeningNum(), true); // bSave=true } }
// Used for adding transaction numbers back to a Nym, after deciding not to use this agreement // or failing in trying to use it. Client side. // void OTAgreement::HarvestClosingNumbers(OTPseudonym & theNym) { // Since we overrode the parent, we give it a chance to harvest also. // If theNym is the sender, then his closing numbers will be harvested // inside here. But what if the transaction was a success? The numbers // will still be harvested, since they are still on the sender's issued // list, but they should not have been harvested, regardless, since the // transaction was a success and the server therefore has them marked as // "used." So clearly you cannot just blindly call this function unless // you know beforehand whether the message and transaction were a success. // OTCronItem::HarvestClosingNumbers(theNym); // The Nym is the original recipient. (If Compares true). // FYI, if Nym is the original sender, then the above call will handle him. // // GetTransactionNum() is burned, but we can harvest the closing // numbers from the "Closing" list, which is only for the sender's numbers. // Subclasses will have to override this function for recipients, etc. // if (theNym.CompareID(GetRecipientUserID())) { for (int i = 0; i < GetRecipientCountClosingNumbers(); i++) { // This function will only "add it back" if it was really there in the first place. // (Verifies it is on issued list first, before adding to available list.) // const bool bClawedBack = theNym.ClawbackTransactionNumber(GetServerID(), GetRecipientClosingTransactionNoAt(i), (i == (GetRecipientCountClosingNumbers()-1) ? true : false)); // bSave=true only on the last iteration. if (!bClawedBack) { // OTLog::vError("OTAgreement::HarvestClosingNumbers: Number (%ld) failed as issued. (Thus didn't bother 'adding it back'.)\n", // GetRecipientClosingTransactionNoAt(i)); } } } }
bool OTAgreement::Compare(const OTAgreement & rhs) const { // Compare OTAgreement specific info here. if ( ( m_strConsideration.Compare(rhs.m_strConsideration) ) && ( GetRecipientAcctID() == rhs.GetRecipientAcctID() ) && ( GetRecipientUserID() == rhs.GetRecipientUserID() ) && // ( m_dequeClosingNumbers == rhs.m_dequeClosingNumbers ) && // The merchant wouldn't know the customer's trans#s. // (Thus wouldn't expect them to be set in BOTH versions...) ( m_dequeRecipientClosingNumbers == rhs.m_dequeRecipientClosingNumbers ) && // ( GetTransactionNum() == rhs.GetTransactionNum() ) && // (commented out for same reason as above.) // ( GetSenderAcctID() == rhs.GetSenderAcctID() ) && // Same here -- we should let the merchant leave these blank, // ( GetSenderUserID() == rhs.GetSenderUserID() ) && // and then allow the customer to add them in his version, ( GetAssetID() == rhs.GetAssetID() ) && // (and this Compare function still still verify it.) ( GetServerID() == rhs.GetServerID() ) && ( GetValidFrom() == rhs.GetValidFrom() ) && ( GetValidTo() == rhs.GetValidTo() ) ) return true; return false; }
// THIS FUNCTION IS CALLED BY THE CUSTOMER // // (Transaction number and closing number are retrieved from Nym at this time.) bool OTAgreement::Confirm(OTPseudonym & MERCHANT_NYM, OTPseudonym & PAYER_NYM) { // ---------------------------------------------------------------------------- OTIdentifier id_MERCHANT_NYM, id_PAYER_NYM; MERCHANT_NYM.GetIdentifier(id_MERCHANT_NYM); PAYER_NYM.GetIdentifier(id_PAYER_NYM); if (GetRecipientUserID() == GetSenderUserID()) { OTLog::Output(0, "OTAgreement::Confirm: Error: Sender and recipient have the same Nym ID (not allowed.)\n"); return false; } else if (GetRecipientUserID() != id_MERCHANT_NYM) { OTLog::Output(0, "OTAgreement::Confirm: Merchant has wrong NymID (should be same as RecipientUserID.)\n"); return false; } else if (GetSenderUserID() != id_PAYER_NYM) { OTLog::Output(0, "OTAgreement::Confirm: Payer has wrong NymID (should be same as SenderUserID.)\n"); return false; } else if (PAYER_NYM.GetTransactionNumCount(GetServerID()) < 2) // Need opening and closing numbers (that's 2)... { OTLog::Output(0, "OTAgreement::Confirm: Failure. You need at least 2 transaction numbers available to do this.\n"); return false; } else if (GetRecipientCountClosingNumbers() < 2) { OTLog::Output(0, "OTAgreement::Confirm: Failure. (The merchant was supposed to attach 2 transaction numbers.)\n"); return false; } // ---------------------------------------------------------------------------- // This is the single reason why MERCHANT_NYM was even passed in here! // Supposedly merchant has already signed. Let's verify this!! // if (false == this->VerifySignature(MERCHANT_NYM)) { OTLog::Output(0, "OTAgreement::Confirm: Merchant's signature failed to verify.\n"); return false; } // ---------------------------------------------------------------------------- // Now that we KNOW the merchant signed it... SAVE MERCHANT's COPY. // Let's save a copy of the one the merchant signed, before changing it and re-signing it, // (to add my own transaction numbers...) // OTString strTemp; this->SaveContractRaw(strTemp); this->SetMerchantSignedCopy(strTemp); // ******************************************************************* // The payer has to submit TWO transaction numbers in order to activate this agreement... // OTString strServerID(GetServerID()); long lTransactionNumber=0, lClosingTransactionNo=0; if (false == PAYER_NYM.GetNextTransactionNum(PAYER_NYM, strServerID, lTransactionNumber)) { OTLog::Error("OTAgreement::Confirm: Error: Strangely unable to get a transaction number.\n"); return false; } else if (false == PAYER_NYM.GetNextTransactionNum(PAYER_NYM, strServerID, lClosingTransactionNo)) { OTLog::Error("OTAgreement::Confirm: Error: Strangely unable to get a closing transaction number.\n"); PAYER_NYM.AddTransactionNum(PAYER_NYM, strServerID, lTransactionNumber, true); // bSave=true // (Since the first one was successful, we just put it back before returning.) return false; } // At this point we now HAVE 2 transaction numbers (for payer / sender)... // We can't return without USING THEM or PUTTING THEM BACK. // // --------------------------------------------------------- this->SetTransactionNum(lTransactionNumber); // Set the Transaction Number this->AddClosingTransactionNo(lClosingTransactionNo); // and the Closing Number (both for sender)... // ------------------------------------------- // CREATION DATE was set in the Merchant's proposal, and it's RESET here in the Confirm. // This way, (since we still have the original proposal) we can see BOTH times. // time_t CURRENT_TIME = time(NULL); // Set the Creation Date. SetCreationDate(CURRENT_TIME); // ------------------------------------------- OTLog::Output(4, "OTAgreement::Confirm(): Success!\n"); return true; }
// THIS FUNCTION IS CALLED BY THE MERCHANT // // (lMerchantTransactionNumber, lMerchantClosingNumber are set internally in this call, from MERCHANT_NYM.) bool OTAgreement::SetProposal(OTPseudonym & MERCHANT_NYM, const OTString & strConsideration, const time_t VALID_FROM/*=0*/, const time_t VALID_TO/*=0*/) { // ---------------------------------------------------------------------------- OTIdentifier id_MERCHANT_NYM; MERCHANT_NYM.GetIdentifier(id_MERCHANT_NYM); if (GetRecipientUserID() != id_MERCHANT_NYM) { OTLog::Output(0, "OTAgreement::SetProposal: Merchant has wrong NymID (should be same as RecipientUserID.)\n"); return false; } else if (GetRecipientUserID() == GetSenderUserID()) { OTLog::Output(0, "OTAgreement::SetProposal: Error: Sender and recipient have the same Nym ID (not allowed.)\n"); return false; } else if (MERCHANT_NYM.GetTransactionNumCount(GetServerID()) < 2) // Need opening and closing numbers (that's 2)... { OTLog::Output(0, "OTAgreement::SetProposal: Failure. You need at least 2 transaction numbers available to do this.\n"); return false; } // ------------------------------------------- // Set the CREATION DATE // const time_t CURRENT_TIME = time(NULL); // Set the Creation Date. SetCreationDate(CURRENT_TIME); // ----------------------------------------- // Putting this above here so I don't have to put the transaction numbers back if this fails: // ------------------------------------------- // VALID_FROM // // The default "valid from" time is NOW. if (0 >= VALID_FROM) // if it's 0 or less, set to current time. SetValidFrom(CURRENT_TIME); else // Otherwise use whatever was passed in. SetValidFrom(VALID_FROM); // ------------------------------------------- // VALID_TO // // The default "valid to" time is 0 (which means no expiration date / cancel anytime.) if (0 == VALID_TO) // VALID_TO is 0 { SetValidTo(VALID_TO); // Keep it at zero then, so it won't expire. } else if (0 < VALID_TO) // VALID_TO is ABOVE zero... { if (VALID_TO < VALID_FROM) // If Valid-To date is EARLIER than Valid-From date... { long lValidTo = VALID_TO, lValidFrom = VALID_FROM; OTLog::vError("OTAgreement::SetProposal: VALID_TO (%ld) is earlier than VALID_FROM (%ld)\n", lValidTo, lValidFrom); return false; } SetValidTo(VALID_TO); // Set it to whatever it is, since it is now validated as higher than Valid-From. } else // VALID_TO is a NEGATIVE number... Error. { long lValidTo = VALID_TO; OTLog::vError("Negative value for valid_to in SetAgreement: %ld\n", lValidTo); return false; } // ---------------------------------------------------------------------------- // Since we'll be needing 2 transaction numbers to do this, let's grab 'em... // OTString strServerID(GetServerID()); long lTransactionNumber=0, lClosingTransactionNo=0; if (MERCHANT_NYM.GetTransactionNumCount(GetServerID()) < 2) // Need opening and closing numbers (that's 2)... { OTLog::Output(0, "OTAgreement::SetProposal: Failure. You need at least 2 transaction numbers available to do this.\n"); return false; } else if (false == MERCHANT_NYM.GetNextTransactionNum(MERCHANT_NYM, strServerID, lTransactionNumber)) { OTLog::Error("OTAgreement::SetProposal: Error: Strangely unable to get a transaction number.\n"); return false; } else if (false == MERCHANT_NYM.GetNextTransactionNum(MERCHANT_NYM, strServerID, lClosingTransactionNo)) { OTLog::Error("OTAgreement::SetProposal: Error: Strangely unable to get a closing transaction number.\n"); MERCHANT_NYM.AddTransactionNum(MERCHANT_NYM, strServerID, lTransactionNumber, true); // bSave=true // (Since the first one was successful, we just put it back before returning.) return false; } // At this point we now have 2 transaction numbers... // We can't return without either USING THEM, or PUTTING THEM BACK. // // --------------------------------------------------------- // Set the Transaction Number and the Closing transaction number... (for merchant / recipient.) // this->AddRecipientClosingTransactionNo(lTransactionNumber); this->AddRecipientClosingTransactionNo(lClosingTransactionNo); // (They just both go onto this same list.) // ------------------------------------------- // Set the Consideration memo... m_strConsideration.Set(strConsideration); // ------------------------------------------- OTLog::Output(4, "Successfully performed OTPaymentPlan::SetProposal()\n"); return true; }
// This is called by OTCronItem::HookRemovalFromCron // (After calling this method, HookRemovalFromCron then calls onRemovalFromCron.) // void OTAgreement::onFinalReceipt(OTCronItem & theOrigCronItem, const long & lNewTransactionNumber, OTPseudonym & theOriginator, OTPseudonym * pRemover) { OTCron * pCron = GetCron(); OT_ASSERT(NULL != pCron); OTPseudonym * pServerNym = pCron->GetServerNym(); OT_ASSERT(NULL != pServerNym); // ------------------------------------------------- // The finalReceipt Item's ATTACHMENT contains the UPDATED Cron Item. // (With the SERVER's signature on it!) // OTString strUpdatedCronItem(*this); OTString * pstrAttachment=&strUpdatedCronItem; const OTString strOrigCronItem(theOrigCronItem); // ----------------------------------------------------------------- OTPseudonym theRecipientNym; // Don't use this... use the pointer just below. // The Nym who is actively requesting to remove a cron item will be passed in as pRemover. // However, sometimes there is no Nym... perhaps it just expired and pRemover is NULL. // The originating Nym (if different than remover) is loaded up. Otherwise the originator // pointer just pointers to *pRemover. // OTPseudonym * pRecipient = NULL; if (pServerNym->CompareID(this->GetRecipientUserID())) { pRecipient = pServerNym; // Just in case the recipient Nym is also the server Nym. } // ******************************************************* // // If pRemover is NOT NULL, and he has the Recipient's ID... // then set the pointer accordingly. // else if ((NULL != pRemover) && (true == pRemover->CompareID(this->GetRecipientUserID()))) { pRecipient = pRemover; // <======== now both pointers are set (to same Nym). DONE! } // -------------------------------------------------------------------------------------------------- if (NULL == pRecipient) { // GetSenderUserID() should be the same on THIS (updated version of the same cron item) // but for whatever reason, I'm checking the userID on the original version. Sue me. // const OTIdentifier NYM_ID(this->GetRecipientUserID()); theRecipientNym.SetIdentifier(NYM_ID); if (false == theRecipientNym.LoadPublicKey()) { OTString strNymID(NYM_ID); OTLog::vError("OTAgreement::onFinalReceipt: Failure loading Recipient's public key:\n%s\n", strNymID.Get()); } else if (theRecipientNym.VerifyPseudonym() && theRecipientNym.LoadSignedNymfile(*pServerNym)) // ServerNym here is merely the signer on this file. { pRecipient = &theRecipientNym; // <===== } else { OTString strNymID(NYM_ID); OTLog::vError("OTAgreement::onFinalReceipt: Failure verifying Recipient's public key or loading signed nymfile: %s\n", strNymID.Get()); } } // ------------------------------- // First, we are closing the transaction number ITSELF, of this cron item, // as an active issued number on the originating nym. (Changing it to CLOSED.) // // Second, we're verifying the CLOSING number, and using it as the closing number // on the FINAL RECEIPT (with that receipt being "InReferenceTo" this->GetTransactionNum()) // const long lRecipientOpeningNumber = this->GetRecipientOpeningNum(); const long lRecipientClosingNumber = this->GetRecipientClosingNum(); // ----------------------------------------------------------------------------------- const long lSenderOpeningNumber = theOrigCronItem.GetTransactionNum(); const long lSenderClosingNumber = (theOrigCronItem.GetCountClosingNumbers() > 0) ? theOrigCronItem.GetClosingTransactionNoAt(0) : 0; // index 0 is closing number for sender, since GetTransactionNum() is his opening #. // ---------------------------------- const OTString strServerID(GetServerID()); // ----------------------------------------------------------------- // if ((lSenderOpeningNumber > 0) && theOriginator.VerifyIssuedNum(strServerID, lSenderOpeningNumber)) { // The Nym (server side) stores a list of all opening and closing cron #s. // So when the number is released from the Nym, we also take it off that list. // std::set<long> & theIDSet = theOriginator.GetSetOpenCronItems(); theIDSet.erase(lSenderOpeningNumber); // the RemoveIssued call means the original transaction# (to find this cron item on cron) is now CLOSED. // But the Transaction itself is still OPEN. How? Because the CLOSING number is still signed out. // The closing number is also USED, since the NotarizePaymentPlan or NotarizeMarketOffer call, but it // remains ISSUED, until the final receipt itself is accepted during a process inbox. // theOriginator.RemoveIssuedNum(*pServerNym, strServerID, lSenderOpeningNumber, false); //bSave=false theOriginator.SaveSignedNymfile(*pServerNym); // ------------------------------------ OTPseudonym * pActualNym = NULL; // use this. DON'T use theActualNym. OTPseudonym theActualNym; // unused unless it's really not already loaded. (use pActualNym.) const OTIdentifier ACTUAL_NYM_ID = GetSenderUserID(); if ( (NULL != pServerNym) && pServerNym->CompareID(ACTUAL_NYM_ID) ) pActualNym = pServerNym; else if (theOriginator.CompareID(ACTUAL_NYM_ID)) pActualNym = &theOriginator; else if ( (NULL != pRemover) && pRemover->CompareID(ACTUAL_NYM_ID) ) pActualNym = pRemover; // -------------------------- else // We couldn't find the Nym among those already loaded--so we have to load { // it ourselves (so we can update its NymboxHash value.) theActualNym.SetIdentifier(ACTUAL_NYM_ID); if (false == theActualNym.LoadPublicKey()) // Note: this step may be unnecessary since we are only updating his Nymfile, not his key. { OTString strNymID(ACTUAL_NYM_ID); OTLog::vError("OTAgreement::onFinalReceipt: Failure loading public key for Nym: %s. " "(To update his NymboxHash.) \n", strNymID.Get()); } else if (theActualNym.VerifyPseudonym() && // this line may be unnecessary. theActualNym.LoadSignedNymfile(*pServerNym)) // ServerNym here is not theActualNym's identity, but merely the signer on this file. { OTLog::Output(0, "OTAgreement::onFinalReceipt: Loading actual Nym, since he wasn't already loaded. " "(To update his NymboxHash.)\n"); pActualNym = &theActualNym; // <===== } else { OTString strNymID(ACTUAL_NYM_ID); OTLog::vError("OTAgreement::onFinalReceipt: Failure loading or verifying Actual Nym public key: %s. " "(To update his NymboxHash.)\n", strNymID.Get()); } } // ------------- if (false == this->DropFinalReceiptToNymbox(GetSenderUserID(), lNewTransactionNumber, strOrigCronItem, NULL, pstrAttachment, pActualNym)) { OTLog::Error("OTAgreement::onFinalReceipt: Failure dropping sender final receipt into nymbox.\n"); } } else { OTLog::Error("OTAgreement::onFinalReceipt: Failure verifying sender's opening number.\n"); } // ----------------------------------------------------------------- if ((lSenderClosingNumber > 0) && theOriginator.VerifyIssuedNum(strServerID, lSenderClosingNumber) ) // --------------------------------------------------------------- { // In this case, I'm passing NULL for pstrNote, since there is no note. // (Additional information would normally be stored in the note.) if (false == this->DropFinalReceiptToInbox(GetSenderUserID(), GetSenderAcctID(), lNewTransactionNumber, lSenderClosingNumber, // The closing transaction number to put on the receipt. strOrigCronItem, NULL, pstrAttachment)) OTLog::Error("OTAgreement::onFinalReceipt: Failure dropping receipt into sender's inbox.\n"); // This part below doesn't happen until theOriginator ACCEPTS the final receipt (when processing his inbox.) // // theOriginator.RemoveIssuedNum(strServerID, lSenderClosingNumber, true); //bSave=false } else { OTLog::Error("OTAgreement::onFinalReceipt: Failed verifying lSenderClosingNumber=theOrigCronItem.GetClosingTransactionNoAt(0)>0 && " "theOriginator.VerifyTransactionNum(lSenderClosingNumber)\n"); } // ----------------------------------------------------------------- // if ((NULL != pRecipient) && (lRecipientOpeningNumber > 0) && pRecipient->VerifyIssuedNum(strServerID, lRecipientOpeningNumber) ) { // The Nym (server side) stores a list of all opening and closing cron #s. // So when the number is released from the Nym, we also take it off that list. // std::set<long> & theIDSet = pRecipient->GetSetOpenCronItems(); theIDSet.erase(lRecipientOpeningNumber); // the RemoveIssued call means the original transaction# (to find this cron item on cron) is now CLOSED. // But the Transaction itself is still OPEN. How? Because the CLOSING number is still signed out. // The closing number is also USED, since the NotarizePaymentPlan or NotarizeMarketOffer call, but it // remains ISSUED, until the final receipt itself is accepted during a process inbox. // pRecipient->RemoveIssuedNum(*pServerNym, strServerID, lRecipientOpeningNumber, false); //bSave=false // pRecipient->SaveSignedNymfile(*pServerNym); // Moved lower. // ----------------------------------------------------- if (false == this->DropFinalReceiptToNymbox(GetRecipientUserID(), lNewTransactionNumber, strOrigCronItem, NULL, pstrAttachment, pRecipient)) // NymboxHash is updated here in pRecipient. { OTLog::Error("OTAgreement::onFinalReceipt: Failure dropping recipient final receipt into nymbox.\n"); } // ----------------------------------------------------- // Saving both the Removed Issued Number, as well as the new NymboxHash. // NOTE: Todo: if the NymboxHash WAS updated (as it should have been) then // it was probably saved at that time. Below is therefore a redundant save. // Need to fix by making certain objects savable and dirty, and then let them // autosave before destruction, IF they are dirty. // pRecipient->SaveSignedNymfile(*pServerNym); } else { OTLog::Error("OTAgreement::onFinalReceipt: Failed verifying " "lRecipientClosingNumber=this->GetRecipientClosingTransactionNoAt(1)>0 && " "pRecipient->VerifyTransactionNum(lRecipientClosingNumber) && VerifyIssuedNum(lRecipientOpeningNumber)\n"); } // ----------------------------------------------------------------- if ((NULL != pRecipient) && (lRecipientClosingNumber > 0) && pRecipient->VerifyIssuedNum(strServerID, lRecipientClosingNumber) ) { if (false == this->DropFinalReceiptToInbox(GetRecipientUserID(), GetRecipientAcctID(), lNewTransactionNumber, lRecipientClosingNumber, // The closing transaction number to put on the receipt. strOrigCronItem, NULL, pstrAttachment)) OTLog::Error("OTAgreement::onFinalReceipt: Failure dropping receipt into recipient's inbox.\n"); // This part below doesn't happen until pRecipient ACCEPTs the final receipt (when processing his inbox.) // // pRecipient->RemoveIssuedNum(strServerID, lRecipientClosingNumber, true); //bSave=false } else { OTLog::Error("OTAgreement::onFinalReceipt: Failed verifying " "lRecipientClosingNumber=this->GetRecipientClosingTransactionNoAt(1)>0 && " "pRecipient->VerifyTransactionNum(lRecipientClosingNumber) && VerifyIssuedNum(lRecipientOpeningNumber)\n"); } // QUESTION: Won't there be Cron Items that have no asset account at all? // In which case, there'd be no need to drop a final receipt, but I don't think // that's the case, since you have to use a transaction number to get onto cron // in the first place. // ----------------------------------------------------------------- }