From ee9d6b27b2072927d5d5b4f3864e12c66366945b Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 4 Jul 2012 01:19:31 -0700 Subject: [PATCH] TransactionEngine support for simple direct ripple sends. --- src/TransactionEngine.cpp | 130 ++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 28 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 1201f1ac8f..d21010c4ce 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -85,7 +85,10 @@ bool transResultInfo(TransactionEngineResult terCode, std::string& strToken, std return iIndex >= 0; } -// We return the uNodeDir so that on delete we can quickly know where the element is mentioned in the directory. +// <-> accounts: Affected accounts for the transaction. +// <-- uNodeDir: For deletion, present to make dirDelete efficient. +// --> uBase: The index of the base of the directory. Nodes are based off of this. +// --> uLedgerIndex: Value to add to directory. TransactionEngineResult TransactionEngine::dirAdd( std::vector& accounts, uint64& uNodeDir, @@ -111,7 +114,6 @@ TransactionEngineResult TransactionEngine::dirAdd( sleRoot = boost::make_shared(ltDIR_ROOT); - sleRoot->setIndex(uRootIndex); Log(lsTRACE) << "dirAdd: Creating dir index: " << sleRoot->getIndex().ToString(); @@ -183,6 +185,10 @@ TransactionEngineResult TransactionEngine::dirAdd( return terSUCCESS; } +// <-> accounts: Affected accounts for the transaction. +// --> uNodeDir: Node containing entry. +// --> uBase: The index of the base of the directory. Nodes are based off of this. +// --> uLedgerIndex: Value to add to directory. TransactionEngineResult TransactionEngine::dirDelete( std::vector& accounts, const uint64& uNodeDir, @@ -883,20 +889,21 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti const uint160& uSrcAccountID) { TransactionEngineResult terResult = terSUCCESS; - std::cerr << "doCreditSet>" << std::endl; + Log(lsINFO) << "doCreditSet>"; // Check if destination makes sense. uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); if (!uDstAccountID) { - std::cerr << "doCreditSet: Invalid transaction: Payment destination account not specifed." << std::endl; + Log(lsINFO) << "doCreditSet: Invalid transaction: Destination account not specifed."; + return tenDST_NEEDED; } - // XXX Might make sense for ripple. else if (uSrcAccountID == uDstAccountID) { - std::cerr << "doCreditSet: Invalid transaction: Source account is the same as destination." << std::endl; + Log(lsINFO) << "doCreditSet: Invalid transaction: Can not extend credit to self."; + return tenDST_IS_SRC; } @@ -904,7 +911,7 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti SLE::pointer sleDst = mLedger->getAccountRoot(qry, uDstAccountID); if (!sleDst) { - std::cerr << "doCreditSet: Delay transaction: Destination account does not exist." << std::endl; + Log(lsINFO) << "doCreditSet: Delay transaction: Destination account does not exist."; return terNO_DST; } @@ -914,41 +921,69 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti bool bSltD = uSrcAccountID < uDstAccountID; uint32 uFlags = bSltD ? lsfLowIndexed : lsfHighIndexed; STAmount saBalance(uCurrency); - bool bAddIndex; + bool bAddIndex = false; + bool bDelIndex = false; qry = lepNONE; SLE::pointer sleRippleState = mLedger->getRippleState(qry, uSrcAccountID, uDstAccountID, uCurrency); if (sleRippleState) { - bAddIndex = !(sleRippleState->getFlags() & uFlags); + // A line exists in one or more directions. +#if 0 + if (saLimitAmount.isZero()) + { + // Zeroing line. + uint160 uLowID = sleRippleState->getIValueFieldAccount(sfLowID).getAccountID(); + uint160 uHighID = sleRippleState->getIValueFieldAccount(sfHighID).getAccountID(); + bool bLow = uLowID == uSrcAccountID; + bool bHigh = uLowID == uDstAccountID; + bool bBalanceZero = sleRippleState->getIValueFieldAmount(sfBalance).isZero(); + STAmount saDstLimit = sleRippleState->getIValueFieldAmount(bSendLow ? sfLowLimit : sfHighLimit); + bool bDstLimitZero = saDstLimit.isZero(); - std::cerr << "doCreditSet: Modifying ripple line: bAddIndex=" << bAddIndex << std::endl; + assert(bLow || bHigh); - sleRippleState->setIFieldAmount(bSltD ? sfLowLimit : sfHighLimit, saLimitAmount); + if (bBalanceZero && bDstLimitZero) + { + // Zero balance and eliminating last limit. - if (bAddIndex) - sleRippleState->setFlag(uFlags); + bDelIndex = true; + terResult = dirDelete(accounts, uSrcRef, Ledger::getRippleDirIndex(uSrcAccountID), sleRippleState->getIndex()); + } + } +#endif + if (!bDelIndex) + { + bAddIndex = !(sleRippleState->getFlags() & uFlags); - accounts.push_back(std::make_pair(taaMODIFY, sleRippleState)); + sleRippleState->setIFieldAmount(bSltD ? sfLowLimit : sfHighLimit, saLimitAmount); + + if (bAddIndex) + sleRippleState->setFlag(uFlags); + + accounts.push_back(std::make_pair(taaMODIFY, sleRippleState)); + } + + Log(lsINFO) << "doCreditSet: Modifying ripple line: bAddIndex=" << bAddIndex << " bDelIndex=" << bDelIndex; } // Line does not exist. else if (saLimitAmount.isZero()) { - std::cerr << "doCreditSet: Setting non-existant ripple line to 0." << std::endl; + Log(lsINFO) << "doCreditSet: Redundant: Setting non-existant ripple line to 0."; return terNO_LINE_NO_ZERO; } else { + // Create a new ripple line. STAmount saZero(uCurrency); bAddIndex = true; sleRippleState = boost::make_shared(ltRIPPLE_STATE); sleRippleState->setIndex(Ledger::getRippleStateIndex(uSrcAccountID, uDstAccountID, uCurrency)); - std::cerr << "doCreditSet: Creating ripple line: " - << sleRippleState->getIndex().ToString() - << std::endl; + Log(lsINFO) << "doCreditSet: Creating ripple line: " + << sleRippleState->getIndex().ToString(); sleRippleState->setFlag(uFlags); sleRippleState->setIFieldAmount(sfBalance, saZero); // Zero balance in currency. @@ -962,17 +997,13 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti if (bAddIndex) { - // Add entries so clients can find lines. - // - Client needs to be able to walk who account has given credit to and who has account's credit. - // - Client doesn't need to know every account who has extended credit but it owed nothing. uint64 uSrcRef; // Ignored, ripple_state dirs never delete. - // XXX Verify extend is passing the right bits, not the zero bits. // XXX Make dirAdd more flexiable to take vector. terResult = dirAdd(accounts, uSrcRef, Ledger::getRippleDirIndex(uSrcAccountID), sleRippleState->getIndex()); } - std::cerr << "doCreditSet<" << std::endl; + Log(lsINFO) << "doCreditSet<"; return terResult; } @@ -1109,15 +1140,19 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction return tenDST_NEEDED; } - else if (saDstAmount.isPositive()) + else if (!saDstAmount.isPositive()) { - Log(lsINFO) << "doPayment: Invalid transaction: Redunant transaction."; + Log(lsINFO) << "doPayment: Invalid transaction: bad amount: " << saDstAmount.getCurrencyHuman() << " " << saDstAmount.getText(); return tenBAD_AMOUNT; } else if (uSrcAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths) { - Log(lsINFO) << "doPayment: Invalid transaction: Redunant transaction."; + Log(lsINFO) << boost::str(boost::format("doPayment: Invalid transaction: Redunant transaction: src=%s, dst=%s, src_cur=%s, dst_cur=%s") + % uSrcAccountID.ToString() + % uDstAccountID.ToString() + % uSrcCurrency.ToString() + % uDstCurrency.ToString()); return tenREDUNDANT; } @@ -1197,10 +1232,18 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction if (sleRippleState) { // There is a direct credit-line of some direction. + // - We can always pay IOUs back. + // - We can issue IOUs to the limit. + // XXX Not implemented: + // - Give preference to pushing out IOUs over sender credit limit. + // - Give preference to pushing out IOUs to creating them. + // - Create IOUs as last resort. uint160 uLowID = sleRippleState->getIValueFieldAccount(sfLowID).getAccountID(); uint160 uHighID = sleRippleState->getIValueFieldAccount(sfHighID).getAccountID(); bool bSendHigh = uLowID == uSrcAccountID && uHighID == uDstAccountID; bool bSendLow = uLowID == uDstAccountID && uHighID == uSrcAccountID; + // Flag we need if we end up owing IOUs. + uint32 uFlags = bSendHigh ? lsfLowIndexed : lsfHighIndexed; assert(bSendLow || bSendHigh); @@ -1214,16 +1257,47 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction } saDstBalance += saDstAmount; - if (saDstAmount > saDstLimit) + if (saDstBalance > saDstLimit) { // Would exceed credit limit. // YYY Note: in the future could push out other credits to make payment fit. - Log(lsINFO) << "doPayment: Delay transaction: Over limit."; + Log(lsINFO) << "doPayment: Delay transaction: Over limit: proposed balance=" + << saDstBalance.getText() + << " limit=" + << saDstLimit.getText(); return terOVER_LIMIT; } + if (saDstBalance.isZero()) + { + // XXX May be able to delete indexes for credit limits which are zero. + nothing(); + } + else if (saDstBalance.isNegative()) + { + // dst still has outstanding IOUs, sle already indexed. + nothing(); + } + // src has outstanding IOUs, sle should be indexed. + else if (! (sleRippleState->getFlags() & uFlags)) + { + // Need to add index. + TransactionEngineResult terResult = terSUCCESS; + uint64 uSrcRef; // Ignored, ripple_state dirs never delete. + + terResult = dirAdd(accounts, + uSrcRef, + Ledger::getRippleDirIndex(uSrcAccountID), // The source ended up owing. + sleRippleState->getIndex()); // Adding current entry. + + if (terSUCCESS != terResult) + return terResult; + + sleRippleState->setFlag(uFlags); // Note now indexed. + } + if (bSendHigh) { // Put balance in low terms.