Merge branch 'ripple'

This commit is contained in:
Arthur Britto
2012-08-27 13:16:03 -07:00
2 changed files with 165 additions and 63 deletions

View File

@@ -59,7 +59,11 @@ bool transResultInfo(TransactionEngineResult terCode, std::string& strToken, std
{ temINVALID, "temINVALID", "The transaction is ill-formed" },
{ temREDUNDANT, "temREDUNDANT", "Sends same currency to self." },
{ temRIPPLE_EMPTY, "temRIPPLE_EMPTY", "PathSet with no paths." },
{ temUNKNOWN, "temUNKNOWN", "The transactions requires logic not implemented yet" },
{ temUNCERTAIN, "temUNCERTAIN", "In process of determining result. Never returned." },
{ temUNKNOWN, "temUNKNOWN", "The transactions requires logic not implemented yet." },
{ tepPATH_DRY, "tepPATH_DRY", "Path could not send partial amount." },
{ tepPATH_PARTIAL, "tepPATH_PARTIAL", "Path could not send full amount." },
{ terDIR_FULL, "terDIR_FULL", "Can not add entry to full dir." },
{ terFUNDS_SPENT, "terFUNDS_SPENT", "Can't set password, password set funds already spent." },
@@ -68,8 +72,6 @@ bool transResultInfo(TransactionEngineResult terCode, std::string& strToken, std
{ terNO_DST, "terNO_DST", "The destination does not exist" },
{ terNO_LINE_NO_ZERO, "terNO_LINE_NO_ZERO", "Can't zero non-existant line, destination might make it." },
{ terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." },
{ terPATH_EMPTY, "terPATH_EMPTY", "Path could not send partial amount." },
{ terPATH_PARTIAL, "terPATH_PARTIAL", "Path could not send full amount." },
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction" },
{ terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." },
{ terUNFUNDED, "terUNFUNDED", "Source account had insufficient balance for transaction." },
@@ -420,14 +422,14 @@ STAmount TransactionEngine::accountSend(const uint160& uSenderID, const uint160&
TransactionEngineResult TransactionEngine::offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID)
{
uint64 uOwnerNode = sleOffer->getIFieldU64(sfOwnerNode);
TransactionEngineResult terResult = dirDelete(false, uOwnerNode, Ledger::getOwnerDirIndex(uOwnerID), uOfferIndex);
TransactionEngineResult terResult = dirDelete(false, uOwnerNode, Ledger::getOwnerDirIndex(uOwnerID), uOfferIndex, false);
if (tesSUCCESS == terResult)
{
uint256 uDirectory = sleOffer->getIFieldH256(sfBookDirectory);
uint64 uBookNode = sleOffer->getIFieldU64(sfBookNode);
terResult = dirDelete(false, uBookNode, uDirectory, uOfferIndex);
terResult = dirDelete(false, uBookNode, uDirectory, uOfferIndex, true);
}
entryDelete(sleOffer);
@@ -435,6 +437,14 @@ TransactionEngineResult TransactionEngine::offerDelete(const SLE::pointer& sleOf
return terResult;
}
TransactionEngineResult TransactionEngine::offerDelete(const uint256& uOfferIndex)
{
SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex);
const uint160 uOwnerID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID();
return offerDelete(sleOffer, uOfferIndex, uOwnerID);
}
// <-- uNodeDir: For deletion, present to make dirDelete efficient.
// --> uRootIndex: The index of the base of the directory. Nodes are based off of this.
// --> uLedgerIndex: Value to add to directory.
@@ -528,16 +538,13 @@ TransactionEngineResult TransactionEngine::dirAdd(
return tesSUCCESS;
}
// --> bKeepRoot: True, if we never completely clean up, after we overflow the root node.
// --> uNodeDir: Node containing entry.
// --> uRootIndex: The index of the base of the directory. Nodes are based off of this.
// --> uLedgerIndex: Value to add to directory.
// Ledger must be in a state for this to work.
TransactionEngineResult TransactionEngine::dirDelete(
bool bKeepRoot,
const uint64& uNodeDir,
const uint256& uRootIndex,
const uint256& uLedgerIndex)
const bool bKeepRoot, // --> True, if we never completely clean up, after we overflow the root node.
const uint64& uNodeDir, // --> Node containing entry.
const uint256& uRootIndex, // --> The index of the base of the directory. Nodes are based off of this.
const uint256& uLedgerIndex, // --> Value to add to directory.
const bool bStable) // --> True, not to change relative order of entries.
{
uint64 uNodeCur = uNodeDir;
SLE::pointer sleNode = entryCache(ltDIR_NODE, uNodeCur ? Ledger::getDirNodeIndex(uRootIndex, uNodeCur) : uRootIndex);
@@ -569,9 +576,21 @@ TransactionEngineResult TransactionEngine::dirDelete(
// Remove the element.
if (vuiIndexes.size() > 1)
*it = vuiIndexes[vuiIndexes.size()-1];
vuiIndexes.resize(vuiIndexes.size()-1);
{
if (bStable)
{
vuiIndexes.erase(it);
}
else
{
*it = vuiIndexes[vuiIndexes.size()-1];
vuiIndexes.resize(vuiIndexes.size()-1);
}
}
else
{
vuiIndexes.clear();
}
sleNode->setIFieldV256(sfIndexes, svIndexes);
entryModify(sleNode);
@@ -1294,7 +1313,7 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran
mTxnAccount = SLE::pointer();
mNodes.clear();
mUnfunded.clear();
musUnfundedFound.clear();
return terResult;
}
@@ -1518,7 +1537,7 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti
// Zero balance and eliminating last limit.
bDelIndex = true;
terResult = dirDelete(false, uSrcRef, Ledger::getOwnerDirIndex(mTxnAccountID), sleRippleState->getIndex());
terResult = dirDelete(false, uSrcRef, Ledger::getOwnerDirIndex(mTxnAccountID), sleRippleState->getIndex(), false);
}
}
#endif
@@ -1860,7 +1879,7 @@ void TransactionEngine::calcOfferBridgeNext(
if (!bDone)
{
// mUnfunded.insert(uOfferIndex);
// musUnfundedFound.insert(uOfferIndex);
}
}
while (bNext);
@@ -3611,6 +3630,9 @@ void TransactionEngine::pathNext(PathState::pointer pspCur, int iPaths)
assert(pspCur->vpnNodes.size() >= 2);
pspCur->vUnfundedBecame.clear();
pspCur->umSource.clear();
pspCur->bValid = calcNode(uLast, pspCur, iPaths == 1);
Log(lsINFO) << "pathNext: bValid="
@@ -3796,8 +3818,8 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction
STAmount saWanted;
LedgerEntrySet lesBase = mNodes; // Checkpoint with just fees paid.
terResult = temUNKNOWN;
while (temUNKNOWN == terResult)
terResult = temUNCERTAIN;
while (temUNCERTAIN == terResult)
{
PathState::pointer pspBest;
LedgerEntrySet lesCheckpoint = mNodes;
@@ -3820,28 +3842,37 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction
{
// Apply best path.
// Install ledger for best past.
// Record best pass' offers that became unfunded for deletion on success.
mvUnfundedBecame.insert(mvUnfundedBecame.end(), pspBest->vUnfundedBecame.begin(), pspBest->vUnfundedBecame.end());
// Record best pass' LedgerEntrySet to build off of and potentially return.
mNodes.swapWith(pspBest->lesEntries);
// Figure out if done.
if (temUNKNOWN == terResult && saPaid == saWanted)
if (temUNCERTAIN == terResult && saPaid == saWanted)
{
terResult = tesSUCCESS;
}
else
{
// Prepare for next pass.
// Merge best pass' umSource.
mumSource.insert(pspBest->umSource.begin(), pspBest->umSource.end());
}
}
// Not done and ran out of paths.
else if (!bPartialPayment)
{
// Partial payment not allowed.
terResult = terPATH_PARTIAL;
terResult = tepPATH_PARTIAL;
mNodes = lesBase; // Revert to just fees charged.
}
// Partial payment ok.
else if (!saPaid)
{
// No payment at all.
// XXX Mark for retry?
terResult = terPATH_EMPTY;
terResult = tepPATH_DRY;
mNodes = lesBase; // Revert to just fees charged.
}
else
@@ -3850,6 +3881,23 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction
}
}
if (tesSUCCESS == terResult)
{
// Delete became unfunded offers.
BOOST_FOREACH(const uint256& uOfferIndex, mvUnfundedBecame)
{
if (tesSUCCESS == terResult)
terResult = offerDelete(uOfferIndex);
}
}
// Delete found unfunded offers.
BOOST_FOREACH(const uint256& uOfferIndex, musUnfundedFound)
{
if (tesSUCCESS == terResult)
terResult = offerDelete(uOfferIndex);
}
std::string strToken;
std::string strHuman;
@@ -3956,12 +4004,16 @@ TransactionEngineResult TransactionEngine::takeOffers(
const uint160 uTakerPaysCurrency = saTakerPays.getCurrency();
const uint160 uTakerGetsAccountID = saTakerGets.getIssuer();
const uint160 uTakerGetsCurrency = saTakerGets.getCurrency();
TransactionEngineResult terResult = temUNKNOWN;
TransactionEngineResult terResult = temUNCERTAIN;
boost::unordered_set<uint256> usOfferUnfundedFound; // Offers found unfunded.
boost::unordered_set<uint256> usOfferUnfundedBecame; // Offers that became unfunded.
boost::unordered_set<uint160> usAccountTouched; // Accounts touched.
saTakerPaid = 0;
saTakerGot = 0;
while (temUNKNOWN == terResult)
while (temUNCERTAIN == terResult)
{
SLE::pointer sleOfferDir;
uint64 uTipQuality;
@@ -3988,7 +4040,9 @@ TransactionEngineResult TransactionEngine::takeOffers(
}
}
if (!sleOfferDir || uTakeQuality < uTipQuality || (bPassive && uTakeQuality == uTipQuality))
if (!sleOfferDir // No offer directory to take.
|| uTakeQuality < uTipQuality // No offer's of sufficient quality available.
|| (bPassive && uTakeQuality == uTipQuality))
{
// Done.
Log(lsINFO) << "takeOffers: done";
@@ -3997,7 +4051,7 @@ TransactionEngineResult TransactionEngine::takeOffers(
}
else
{
// Have an offer to consider.
// Have an offer directory to consider.
Log(lsINFO) << "takeOffers: considering dir : " << sleOfferDir->getJson(0);
SLE::pointer sleBookNode;
@@ -4016,19 +4070,17 @@ TransactionEngineResult TransactionEngine::takeOffers(
if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC())
{
// Offer is expired. Delete it.
// Offer is expired. Expired offers are considered unfunded. Delete it.
Log(lsINFO) << "takeOffers: encountered expired offer";
offerDelete(sleOffer, uOfferIndex, uOfferOwnerID);
mUnfunded.insert(uOfferIndex);
usOfferUnfundedFound.insert(uOfferIndex);
}
else if (uOfferOwnerID == uTakerAccountID)
{
// Would take own offer. Consider old offer unfunded.
// Would take own offer. Consider old offer expired. Delete it.
Log(lsINFO) << "takeOffers: encountered taker's own old offer";
offerDelete(sleOffer, uOfferIndex, uOfferOwnerID);
usOfferUnfundedFound.insert(uOfferIndex);
}
else
{
@@ -4045,9 +4097,17 @@ TransactionEngineResult TransactionEngine::takeOffers(
// Offer is unfunded, possibly due to previous balance action.
Log(lsINFO) << "takeOffers: offer unfunded: delete";
offerDelete(sleOffer, uOfferIndex, uOfferOwnerID);
mUnfunded.insert(uOfferIndex);
boost::unordered_set<uint160>::iterator account = usAccountTouched.find(uOfferOwnerID);
if (account != usAccountTouched.end())
{
// Previously touched account.
usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success.
}
else
{
// Never touched source account.
usOfferUnfundedFound.insert(uOfferIndex); // Delete found unfunded offer when possible.
}
}
else
{
@@ -4081,24 +4141,28 @@ TransactionEngineResult TransactionEngine::takeOffers(
Log(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText();
// Adjust offer
// Offer owner will pay less. Subtract what taker just got.
sleOffer->setIFieldAmount(sfTakerGets, saOfferPays -= saSubTakerGot);
// Offer owner will get less. Subtract what owner just paid.
sleOffer->setIFieldAmount(sfTakerPays, saOfferGets -= saSubTakerPaid);
entryModify(sleOffer);
if (bOfferDelete)
{
// Offer now fully claimed or now unfunded.
Log(lsINFO) << "takeOffers: offer claimed: delete";
offerDelete(sleOffer, uOfferIndex, uOfferOwnerID);
usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success.
// Offer owner's account is no longer pristine.
usAccountTouched.insert(uOfferOwnerID);
}
else
{
Log(lsINFO) << "takeOffers: offer partial claim: modify";
// Offer owner will pay less. Subtract what taker just got.
sleOffer->setIFieldAmount(sfTakerGets, saOfferPays -= saSubTakerGot);
// Offer owner will get less. Subtract what owner just paid.
sleOffer->setIFieldAmount(sfTakerPays, saOfferGets -= saSubTakerPaid);
entryModify(sleOffer);
Log(lsINFO) << "takeOffers: offer partial claim.";
}
// Offer owner pays taker.
@@ -4119,6 +4183,40 @@ TransactionEngineResult TransactionEngine::takeOffers(
}
}
// On storing meta data, delete offers that were found unfunded to prevent encountering them in future.
switch (terResult)
{
case tesSUCCESS:
case tepPATH_DRY:
case tepPATH_PARTIAL:
BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedFound)
{
TransactionEngineResult terDelete = offerDelete(uOfferIndex);
if (tesSUCCESS != terDelete)
terResult = terDelete;
break;
}
break;
default:
nothing();
break;
}
if (tesSUCCESS == terResult)
{
// On success, delete offers that became unfunded.
BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedBecame)
{
TransactionEngineResult terDelete = offerDelete(uOfferIndex);
if (tesSUCCESS != terDelete)
terResult = terDelete;
break;
}
}
return terResult;
}

View File

@@ -40,6 +40,7 @@ enum TransactionEngineResult
temINVALID,
temREDUNDANT,
temRIPPLE_EMPTY,
temUNCERTAIN,
temUNKNOWN,
// -199 .. -100: F Failure (sequence number previously used)
@@ -77,12 +78,9 @@ enum TransactionEngineResult
// 100 .. P Partial success (SR) (ripple transaction with no good paths, pay to non-existent account)
// Transaction can be applied, can charge fee, forwarded, but does not achieve optimal result.
tesPARITAL = 100,
// Might succeed in different order.
// XXX claim fee and try to delete unfunded.
terPATH_EMPTY,
terPATH_PARTIAL,
tepPARITAL = 100,
tepPATH_DRY,
tepPATH_PARTIAL,
};
bool transResultInfo(TransactionEngineResult terCode, std::string& strToken, std::string& strHuman);
@@ -132,9 +130,6 @@ public:
bool bValid;
std::vector<paymentNode> vpnNodes;
// If the transaction fails to meet some constraint, still need to delete unfunded offers.
boost::unordered_set<uint256> usUnfundedFound; // Offers that were found unfunded.
// When processing, don't want to complicate directory walking with deletion.
std::vector<uint256> vUnfundedBecame; // Offers that became unfunded.
@@ -197,10 +192,11 @@ private:
const uint256& uLedgerIndex);
TransactionEngineResult dirDelete(
bool bKeepRoot,
const bool bKeepRoot,
const uint64& uNodeDir, // Node item is mentioned in.
const uint256& uRootIndex,
const uint256& uLedgerIndex); // Item being deleted
const uint256& uLedgerIndex, // Item being deleted
const bool bStable);
bool dirFirst(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
@@ -224,13 +220,23 @@ protected:
uint160 mTxnAccountID;
SLE::pointer mTxnAccount;
boost::unordered_set<uint256> mUnfunded; // Indexes that were found unfunded.
// First time working in reverse a funding source was mentioned. Source may only be used there.
boost::unordered_map<std::pair<uint160, uint160>, int> mumSource; // Map of currency, issuer to node index.
// When processing, don't want to complicate directory walking with deletion.
std::vector<uint256> mvUnfundedBecame; // Offers that became unfunded.
// If the transaction fails to meet some constraint, still need to delete unfunded offers.
boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded.
SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex);
SLE::pointer entryCache(LedgerEntryType letType, const uint256& uIndex);
void entryDelete(SLE::pointer sleEntry, bool bUnfunded = false);
void entryModify(SLE::pointer sleEntry);
TransactionEngineResult offerDelete(const uint256& uOfferIndex);
TransactionEngineResult offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID);
uint32 rippleTransferRate(const uint160& uIssuerID);
STAmount rippleOwed(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID);
STAmount rippleLimit(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID);
@@ -260,8 +266,6 @@ protected:
void txnWrite();
TransactionEngineResult offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID);
TransactionEngineResult doAccountSet(const SerializedTransaction& txn);
TransactionEngineResult doClaim(const SerializedTransaction& txn);
TransactionEngineResult doCreditSet(const SerializedTransaction& txn);