From 3462ab0f0780da0c2c2af0d153e3624a06fe6527 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 9 Jul 2012 17:38:03 -0700 Subject: [PATCH] Work towards ripple and offers. --- src/Amount.cpp | 51 +++-- src/Ledger.h | 19 +- src/LedgerFormats.cpp | 5 +- src/LedgerFormats.h | 2 +- src/LedgerIndex.cpp | 4 +- src/SerializedObject.h | 7 +- src/SerializedTypes.h | 8 +- src/TransactionEngine.cpp | 418 +++++++++++++++++++++++-------------- src/TransactionEngine.h | 4 +- src/TransactionFormats.cpp | 22 +- src/TransactionFormats.h | 3 +- 11 files changed, 333 insertions(+), 210 deletions(-) diff --git a/src/Amount.cpp b/src/Amount.cpp index a3831035f9..dcce9a8bd8 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -256,10 +256,11 @@ STAmount::STAmount(const char* name, int64 value) : SerializedType(name), mOffse void STAmount::setValue(const STAmount &a) { - mCurrency = a.mCurrency; - mValue = a.mValue; - mOffset = a.mOffset; - mIsNative = a.mIsNative; + mCurrency = a.mCurrency; + mIssuer = a.mIssuer; + mValue = a.mValue; + mOffset = a.mOffset; + mIsNative = a.mIsNative; mIsNegative = a.mIsNegative; } @@ -455,11 +456,13 @@ STAmount STAmount::operator-(void) const STAmount& STAmount::operator=(const STAmount& a) { - mValue = a.mValue; - mOffset = a.mOffset; - mCurrency = a.mCurrency; - mIsNative = a.mIsNative; + mValue = a.mValue; + mOffset = a.mOffset; + mIssuer = a.mIssuer; + mCurrency = a.mCurrency; + mIsNative = a.mIsNative; mIsNegative = a.mIsNegative; + return *this; } @@ -469,6 +472,7 @@ STAmount& STAmount::operator=(uint64 v) mValue = v; mIsNegative = false; if (!mIsNative) canonicalize(); + return *this; } @@ -477,6 +481,7 @@ STAmount& STAmount::operator+=(uint64 v) if (mIsNative) setSNValue(getSNValue() + static_cast(v)); else *this += STAmount(mCurrency, v); + return *this; } @@ -485,6 +490,7 @@ STAmount& STAmount::operator-=(uint64 v) if (mIsNative) setSNValue(getSNValue() - static_cast(v)); else *this -= STAmount(mCurrency, v); + return *this; } @@ -697,15 +703,24 @@ STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint16 else return STAmount(currencyOut, v.getulong(), offset1 + offset2 + 14); } +// Convert an offer into an index amount so they sort by rate. +// A taker will take the best, lowest, rate first. +// (e.g. a taker will prefer pay 1 get 3 over pay 1 get 2. +// --> offerOut: takerGets: How much the offerer is selling to the taker. +// --> offerIn: takerPays: How much the offerer is receiving from the taker. +// <-- uRate: normalize(offerIn/offerOut) +// A lower rate is better for the person taking the order. +// The taker gets more for less with a lower rate. uint64 STAmount::getRate(const STAmount& offerOut, const STAmount& offerIn) -{ // Convert an offer into an index amount so they sort (lower is better) - // offerOut = how much comes out of the offer, from the offeror to the taker - // offerIn = how much goes into the offer, from the taker to the offeror +{ if (offerOut.isZero()) throw std::runtime_error("Worthless offer"); STAmount r = divide(offerIn, offerOut, uint160(1)); + assert((r.getExponent() >= -100) && (r.getExponent() <= 155)); + uint64 ret = r.getExponent() + 100; + return (ret << (64 - 8)) | r.getMantissa(); } @@ -746,11 +761,19 @@ STAmount STAmount::getClaimed(STAmount& offerOut, STAmount& offerIn, STAmount& p return ret; } -STAmount STAmount::getNeeded(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed) +STAmount STAmount::getPay(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed) { // Someone wants to get (needed) out of the offer, how much should they pay in? - if (offerOut.isZero()) return STAmount(offerIn.getCurrency()); - if (needed >= offerOut) return needed; + if (offerOut.isZero()) + return STAmount(offerIn.getCurrency()); + + if (needed >= offerOut) + { + // They need more than offered, pay full amount. + return needed; + } + STAmount ret = divide(multiply(needed, offerIn, uint160(1)), offerOut, offerIn.getCurrency()); + return (ret > offerIn) ? offerIn : ret; } diff --git a/src/Ledger.h b/src/Ledger.h index 2cda039463..93d0d2ff34 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -65,6 +65,7 @@ private: uint256 mHash, mParentHash, mTransHash, mAccountHash; uint64 mTotCoins; uint64 mCloseTime; // when this ledger closes + uint64 mParentCloseTime; uint32 mLedgerSeq; uint16 mLedgerInterval; bool mClosed, mValidHash, mAccepted, mImmutable; @@ -119,6 +120,7 @@ public: uint64 getTotalCoins() const { return mTotCoins; } void destroyCoins(uint64 fee) { mTotCoins -= fee; } uint64 getCloseTimeNC() const { return mCloseTime; } + uint64 getParentCloseTimeNC() const { return mParentCloseTime; } uint32 getLedgerSeq() const { return mLedgerSeq; } uint16 getInterval() const { return mLedgerInterval; } @@ -200,17 +202,24 @@ public: SLE::pointer getOffer(LedgerStateParms& parms, const uint160& uAccountID, uint32 uSequence) { return getOffer(parms, getOfferIndex(uAccountID, uSequence)); } - + // The index of an offer. static uint256 getOfferIndex(const uint160& uAccountID, uint32 uSequence); - static uint256 getOfferDirIndex(const uint160& uAccountID); + // + // Owner functions + // + + // All items controlled by an account are here: offers + static uint256 getOwnerDirIndex(const uint160& uAccountID); // // Directory functions - // + // Directories are doubly linked lists of nodes. + // Given a directory root and and index compute the index of a node. static uint256 getDirNodeIndex(const uint256& uDirRoot, const uint64 uNodeIndex=0); + // Return a node: root or normal SLE::pointer getDirNode(LedgerStateParms& parms, const uint256& uNodeIndex); // @@ -223,12 +232,12 @@ public: // Ripple functions : credit lines // + // Index of node which is the ripple state between to accounts for a currency. static uint256 getRippleStateIndex(const NewcoinAddress& naA, const NewcoinAddress& naB, const uint160& uCurrency); static uint256 getRippleStateIndex(const uint160& uiA, const uint160& uiB, const uint160& uCurrency) { return getRippleStateIndex(NewcoinAddress::createAccountID(uiA), NewcoinAddress::createAccountID(uiB), uCurrency); } - static uint256 getRippleStateIndex(const NewcoinAddress& naA, const NewcoinAddress& naB) - { return getRippleStateIndex(naA, naB, uint160()); } + // Directory of lines indexed by an account (not all lines are indexed) static uint256 getRippleDirIndex(const uint160& uAccountID); RippleState::pointer getRippleState(const uint256& uNode); diff --git a/src/LedgerFormats.cpp b/src/LedgerFormats.cpp index d745c557f2..0c1e15ef98 100644 --- a/src/LedgerFormats.cpp +++ b/src/LedgerFormats.cpp @@ -16,7 +16,8 @@ LedgerEntryFormat LedgerFormats[]= { S_FIELD(EmailHash), STI_HASH128, SOE_IFFLAG, 2 }, { S_FIELD(WalletLocator), STI_HASH256, SOE_IFFLAG, 4 }, { S_FIELD(MessageKey), STI_VL, SOE_IFFLAG, 8 }, - { S_FIELD(TransitRate), STI_UINT32, SOE_IFFLAG, 16 }, + { S_FIELD(TransferRate), STI_UINT32, SOE_IFFLAG, 16 }, + { S_FIELD(Domain), STI_VL, SOE_IFFLAG, 32 }, { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x01000000 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, @@ -49,7 +50,7 @@ LedgerEntryFormat LedgerFormats[]= { S_FIELD(AmountOut), STI_AMOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Expiration), STI_UINT32, SOE_REQUIRED, 0 }, { S_FIELD(OwnerNode), STI_UINT64, SOE_REQUIRED, 0 }, - { S_FIELD(OfferNode), STI_UINT64, SOE_REQUIRED, 0 }, + { S_FIELD(BookNode), STI_UINT64, SOE_REQUIRED, 0 }, { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x01000000 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, diff --git a/src/LedgerFormats.h b/src/LedgerFormats.h index f3d6fae544..230e557c48 100644 --- a/src/LedgerFormats.h +++ b/src/LedgerFormats.h @@ -25,7 +25,7 @@ enum LedgerNameSpace spaceRipple = 'r', spaceRippleDir = 'R', spaceOffer = 'o', // Entry for an offer. - spaceOfferDir = 'O', // Directory of an account's offers. + spaceOwnerDir = 'O', // Directory of things owned by an account. spaceBookDir = 'B', // Directory of order books. spaceBond = 'b', spaceInvoice = 'i', diff --git a/src/LedgerIndex.cpp b/src/LedgerIndex.cpp index a60d522385..0d2071a0ff 100644 --- a/src/LedgerIndex.cpp +++ b/src/LedgerIndex.cpp @@ -99,11 +99,11 @@ uint256 Ledger::getOfferIndex(const uint160& uAccountID, uint32 uSequence) return s.getSHA512Half(); } -uint256 Ledger::getOfferDirIndex(const uint160& uAccountID) +uint256 Ledger::getOwnerDirIndex(const uint160& uAccountID) { Serializer s(22); - s.add16(spaceOfferDir); // 2 + s.add16(spaceOwnerDir); // 2 s.add160(uAccountID); // 20 return s.getSHA512Half(); diff --git a/src/SerializedObject.h b/src/SerializedObject.h index 628219a4c3..5dfcc4694e 100644 --- a/src/SerializedObject.h +++ b/src/SerializedObject.h @@ -34,6 +34,7 @@ enum SOE_Field sfAmountOut, sfAuthorizedKey, sfBalance, + sfBookNode, sfBorrowExpire, sfBorrowRate, sfBorrowStart, @@ -43,6 +44,7 @@ enum SOE_Field sfCurrencyIn, sfCurrencyOut, sfDestination, + sfDomain, sfEmailHash, sfExpiration, sfExtensions, @@ -76,7 +78,6 @@ enum SOE_Field sfNextTransitRate, sfNextTransitStart, sfNickname, - sfOfferNode, sfOfferSequence, sfOwnerNode, sfPaths, @@ -90,9 +91,7 @@ enum SOE_Field sfSourceTag, sfTarget, sfTargetLedger, - sfTransitExpire, - sfTransitRate, - sfTransitStart, + sfTransferRate, sfVersion, sfWalletLocator, diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 5953faed2f..9b97202850 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -207,6 +207,7 @@ class STAmount : public SerializedType protected: uint160 mCurrency; + uint160 mIssuer; // Only for access, not compared. uint64 mValue; int mOffset; @@ -282,7 +283,10 @@ public: void negate() { if (!isZero()) mIsNegative = !mIsNegative; } void zero() { mOffset = mIsNative ? -100 : 0; mValue = 0; mIsNegative = false; } - const uint160& getCurrency() const { return mCurrency; } + const uint160& getIssuer() const { return mIssuer; } + void setIssuer(const uint160& uIssuer) { mIssuer = uIssuer; } + + const uint160& getCurrency() const { return mCurrency; } bool setValue(const std::string& sAmount, const std::string& sCurrency); void setValue(const STAmount &); @@ -329,7 +333,7 @@ public: static STAmount getClaimed(STAmount& offerOut, STAmount& offerIn, STAmount& paid); // Someone is offering X for Y, I need Z, how much do I pay - static STAmount getNeeded(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed); + static STAmount getPay(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed); // Native currency conversions, to/from display format static uint64 convertToDisplayAmount(const STAmount& internalAmount, uint64 totalNow, uint64 totalInit); diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 271fcb31a5..448e0ecbf8 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -61,12 +61,12 @@ bool transResultInfo(TransactionEngineResult terCode, std::string& strToken, std { terINSUF_FEE_T, "terINSUF_FEE_T", "fee insufficient now (account doesn't exist, network load)" }, { terNODE_NOT_FOUND, "terNODE_NOT_FOUND", "Can not delete a directory node." }, { terNODE_NOT_MENTIONED, "terNODE_NOT_MENTIONED", "Could not remove node from a directory." }, - { terNODE_NO_ROOT, "terNODE_NO_ROOT", "Directory doesn't exist." }, + { terNODE_NO_ROOT, "terNODE_NO_ROOT", "Directory doesn't exist." }, { terNO_ACCOUNT, "terNO_ACCOUNT", "The source account does not exist" }, { 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." }, { terNO_PATH, "terNO_PATH", "No path existed or met transaction/balance requirements" }, - { terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." }, + { terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." }, { terOVER_LIMIT, "terOVER_LIMIT", "Over limit." }, { terPAST_LEDGER, "terPAST_LEDGER", "The transaction expired and can't be applied" }, { terPAST_SEQ, "terPAST_SEQ", "This sequence number has already past" }, @@ -452,7 +452,8 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran TransactionEngineParams params, Ledger::pointer ledger) { Log(lsTRACE) << "applyTransaction>"; - mLedger = ledger; + mLedger = ledger; + mLedgerParentCloseTime = mLedger->getParentCloseTimeNC(); #ifdef DEBUG if (1) @@ -536,10 +537,9 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran case ttACCOUNT_SET: case ttCREDIT_SET: case ttINVOICE: - case ttOFFER: + case ttOFFER_CREATE: case ttOFFER_CANCEL: case ttPASSWORD_FUND: - case ttTRANSIT_SET: case ttWALLET_ADD: nothing(); break; @@ -797,8 +797,8 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran result = doInvoice(txn, accounts); break; - case ttOFFER: - result = doOffer(txn, accounts, srcAccountID); + case ttOFFER_CREATE: + result = doOfferCreate(txn, accounts, srcAccountID); break; case ttOFFER_CANCEL: @@ -821,10 +821,6 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran result = doPayment(txn, accounts, srcAccountID); break; - case ttTRANSIT_SET: - result = doTransitSet(txn, accounts); - break; - case ttWALLET_ADD: result = doWalletAdd(txn, accounts); break; @@ -1549,125 +1545,6 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction return terBAD_RIPPLE; } -TransactionEngineResult TransactionEngine::doTransitSet(const SerializedTransaction& st, std::vector&) -{ - std::cerr << "doTransitSet>" << std::endl; -#if 0 - SLE::pointer sleSrc = accounts[0].second; - - bool bTxnTransitRate = st->getIFieldPresent(sfTransitRate); - bool bTxnTransitStart = st->getIFieldPresent(sfTransitStart); - bool bTxnTransitExpire = st->getIFieldPresent(sfTransitExpire); - uint32 uTxnTransitRate = bTxnTransitRate ? st->getIFieldU32(sfTransitRate) : 0; - uint32 uTxnTransitStart = bTxnTransitStart ? st->getIFieldU32(sfTransitStart) : 0; - uint32 uTxnTransitExpire = bTxnTransitExpire ? st->getIFieldU32(sfTransitExpire) : 0; - - bool bActTransitRate = sleSrc->getIFieldPresent(sfTransitRate); - bool bActTransitExpire = sleSrc->getIFieldPresent(sfTransitExpire); - bool bActNextTransitRate = sleSrc->getIFieldPresent(sfNextTransitRate); - bool bActNextTransitStart = sleSrc->getIFieldPresent(sfNextTransitStart); - bool bActNextTransitExpire = sleSrc->getIFieldPresent(sfNextTransitExpire); - uint32 uActTransitRate = bActTransitRate ? sleSrc->getIFieldU32(sfTransitRate) : 0; - uint32 uActTransitExpire = bActTransitExpire ? sleSrc->getIFieldU32(sfTransitExpire) : 0; - uint32 uActNextTransitRate = bActNextTransitRate ? sleSrc->getIFieldU32(sfNextTransitRate) : 0; - uint32 uActNextTransitStart = bActNextTransitStart ? sleSrc->getIFieldU32(sfNextTransitStart) : 0; - uint32 uActNextTransitExpire = bActNextTransitExpire ? sleSrc->getIFieldU32(sfNextTransitExpire) : 0; - - // - // Update view - // - - bool bNoCurrent = !bActTransitRate; - bool bCurrentExpired = - bActTransitExpire // Current can expire - && bActNextTransitStart // Have a replacement - && uActTransitExpire <= uLedger; // Current is expired - - // Replace current with next if need. - if (bNoCurrent // No current. - && bActNextTransitRate // Have next. - && uActNextTransitStart <= uLedger) // Next has started. - { - // Make next current. - uActTransitRate = uActNextTransitRate; - bActTransitExpire = bActNextTransitStart; - uActTransitExpire = uActNextTransitExpire; - - // Remove next. - uActNextTransitStart = 0; - } - - // - // Determine new transaction deposition. - // - - bool bBetterThanCurrent = - !no current - || ( - Expires same or later than current - Start before or same as current - Fee same or less than current - ) - - bool bBetterThanNext = - !no next - || ( - Expires same or later than next - Start before or same as next - Fee same or less than next - ) - - bool bBetterThanBoth = - bBetterThanCurrent && bBetterThanNext - - bool bCurrentBlocks = - !bBetterThanCurrent - && overlaps with current - - bool bNextBlocks = - !bBetterThanNext - && overlaps with next - - if (bBetterThanBoth) - { - // Erase both and install. - - // If not starting now, install as next. - } - else if (bCurrentBlocks || bNextBlocks) - { - // Invalid ignore - } - else if (bBetterThanCurrent) - { - // Install over current - } - else if (bBetterThanNext) - { - // Install over next - } - else - { - // Error. - } - - return tenTRANSIT_WORSE; - - // Set current. - uDstTransitRate = uTxnTransitRate; - uDstTransitExpire = uTxnTransitExpire; // 0 for never expire. - - // Set future. - uDstNextTransitRate = uTxnTransitRate; - uDstNextTransitStart = uTxnTransitStart; - uDstNextTransitExpire = uTxnTransitExpire; // 0 for never expire. - - if (txn.getITFieldPresent(sfCurrency)) -#endif - std::cerr << "doTransitSet<" << std::endl; - return tenINVALID; -} - TransactionEngineResult TransactionEngine::doWalletAdd(const SerializedTransaction& txn, std::vector& accounts) { @@ -1736,10 +1613,168 @@ TransactionEngineResult TransactionEngine::doInvoice(const SerializedTransaction return tenUNKNOWN; } -// XXX Needs to take offers. -// XXX Use bPassive when taking. -// XXX Also use quality when rippling a take. -TransactionEngineResult TransactionEngine::doOffer( +#ifdef WORK_IN_PROGRESS +// XXX Disallow loops in ripple paths +// XXX Note accounts we visited so as not mark them found unfunded. +// Before an offer is place into the ledger, fill as much as possible. +// XXX Also use quality fees when rippling a take. +// XXX Also be careful of taking own offer: delete old offer. +// --> uBookBase: the opposite order book. +TransactionEngineResult TransactionEngine::offerTake( + bool bPassive, + uint64 uTakeQuality, + const uint256& uBookBase, + const uint160& uTakerAccountID, + STAmount& saTakerGets, // With issuer. + STAmount& saTakerPays, // With issuer. + std::vector vspUnfundedFound) +{ + uint256 uTipIndex = uBookIndex; + bool bDone = true; + STAmount saSold = 0; // XXX Add in currency + STAmount saOffered = XXX amount to fill. + TransactionEngineResult terResult = tenUNKNOWN; + + while (tenUNKNOWN == terResult) + { + uTipIndex = Ledger::indexNext(uTipIndex); + + uint256 uTipBase; + uint64 uTipQuality = Ledger::indexQuality(uTipIndex, uTipBase); + + if (saSold == saAmount) + { + // Filled order. + terResult = terSUCCESS; + } + else if (uTipBase != uBookBase + || uTakeQuality < uTipQuality + || (bPassive && uTakeQuality == uTipQuality)) + { + // No qualifying offer. + + terResult = terSUCCESS; + } + else + { + // Have an offer to consider. + LedgerStateParms qry = lepNONE; + SLE::pointer sleOffer = mLedger->getOffer(qry, uTipIndex); + + assert(sleOffer); + if (!sleOffer) + { + // Missing ledger entry. + Log(lsINFO) << "offerTake: Missing offer node: " << uTipIndex.ToString(); + + terResult = terBAD_LEDGER; + } + else + { + NewcoinAddress naOfferAccountID = sleOffer->getIValueFieldAccount(sfAccount); + STAmount saOfferTakerGets = sleOffer->getIValueFieldAmount(sfAmountOut); + + if (naOfferAccountID == uTakerAccountID) + { + // Would take own offer. Consider it unfunded. Delete it. + + vspUnfundedFound.push_back(sleOffer); + } + else if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= prevLedgerClose) + { + // Offer is expired. Delete it. + + vspUnfundedFound.push_back(sleOffer); + } + else + { + SLE::pointer sleOfferAccount; + SLE::pointer sleOfferRipplePays; + STAmount saOfferBalance; + + if (saTakerGets.isNative()) + { + // Handle getting stamps. + LedgerStateParms qry = lepNONE; + SLE::pointer sleOfferAccount = mLedger->getAccountRoot(qry, naOfferAccountID); + if (!sleOfferAccount) + { + Log(lsWARNING) << "offerTake: delay: can't receive stamps from non-existant account"; + + terResult = terNO_ACCOUNT; + } + else + { + saOfferBalance = sleOfferAccount->getIValueFieldAmount(sfBalance); + } + } + else + { + // Handling getting ripple. + + if (saTakerGets.getIssuer() == naOfferAccountID) + { + // Taker gets offer's IOUs from offerer. Always works + + } + else + { + sleOfferRipplePays = getRippleState(getRippleStateIndex(uSrcAccountID, saTakerGets.getIssuer(), saTakerGets.getCurrency())); + + bool bSltD = uSrcAccountID < uIssuerOutID; + + STAmount saSrcBalance = sleRippleState->getIValueFieldAmount(sfBalance); + if (bSltD) + saSrcBalance.negate(); // Put balance in low terms. + } + STAmount saSrcBalance = sleOfferAccount->getIValueFieldAmount(sfBalance); + + if (saSrcBalance.isZero()) + { + terResult = terUNFUNDED; + } + else + { + STAmount saTakerPaysCur = STAmount::getPay(saOfferTakerGets, saOfferTakerPays, saTakerWants); + STAmount saTakerGetsCur = STAmount::getClaimed(saOfferTakerGets, saOfferTakerPays, saTakerPays); + + saTakerWants -= saTakerGetsCur; + + sleOfferAccount->setIFieldAmount(sfBalance, saSrcBalance - saPaid); + } + } + + } + // Handle getting IOUs. + else + + if (saSrcBalance.isPositive()) + { + + } + == saOoffer is unfunded + else + figure out how much to convert + + note to counter party how much taken + + if took it all + deleteIt + else + makeChanges + } + } + else + { + bDone = true; + } + } + + return tenUNKNOWN == terResult ? terSUCCESS : terResult; +} +#endif + +TransactionEngineResult TransactionEngine::doOfferCreate( const SerializedTransaction& txn, std::vector& accounts, const uint160& uSrcAccountID) @@ -1748,8 +1783,8 @@ TransactionEngineResult TransactionEngine::doOffer( bool bPassive = !!(txFlags & tfPassive); STAmount saAmountIn = txn.getITFieldAmount(sfAmountIn); STAmount saAmountOut = txn.getITFieldAmount(sfAmountOut); - uint160 uIssuerIn = txn.getITFieldAccount(sfIssuerIn); - uint160 uIssuerOut = txn.getITFieldAccount(sfIssuerOut); + uint160 uIssuerInID = txn.getITFieldAccount(sfIssuerIn); + uint160 uIssuerOutID = txn.getITFieldAccount(sfIssuerOut); uint32 uExpiration = txn.getITFieldU32(sfExpiration); bool bHaveExpiration = txn.getITFieldPresent(sfExpiration); uint32 uSequence = txn.getSequence(); @@ -1758,72 +1793,133 @@ TransactionEngineResult TransactionEngine::doOffer( SLE::pointer sleOffer = boost::make_shared(ltOFFER); uint256 uLedgerIndex = Ledger::getOfferIndex(uSrcAccountID, uSequence); - Log(lsINFO) << "doOffer: Creating offer node: " << uLedgerIndex.ToString(); + Log(lsINFO) << "doOfferCreate: Creating offer node: " << uLedgerIndex.ToString(); uint160 uCurrencyIn = saAmountIn.getCurrency(); uint160 uCurrencyOut = saAmountOut.getCurrency(); - TransactionEngineResult terResult; + TransactionEngineResult terResult = terSUCCESS; uint64 uOwnerNode; // Delete hint. - uint64 uOfferNode; // Delete hint. - // uint64 uBookNode; // Delete hint. + uint64 uBookNode; // Delete hint. uint32 uPrevLedgerTime = 0; // XXX Need previous if (!bHaveExpiration || !uExpiration) { - Log(lsWARNING) << "doOffer: Malformed offer: bad expiration"; + Log(lsWARNING) << "doOfferCreate: Malformed offer: bad expiration"; terResult = tenBAD_EXPIRATION; } else if (!bHaveExpiration || uPrevLedgerTime >= uExpiration) { - Log(lsWARNING) << "doOffer: Expired transaction: offer expired"; + Log(lsWARNING) << "doOfferCreate: Expired transaction: offer expired"; terResult = tenEXPIRED; } else if (saAmountIn.isNative() && saAmountOut.isNative()) { - Log(lsWARNING) << "doOffer: Malformed offer: stamps for stamps"; + Log(lsWARNING) << "doOfferCreate: Malformed offer: stamps for stamps"; terResult = tenBAD_OFFER; } else if (saAmountIn.isZero() || saAmountOut.isZero()) { - Log(lsWARNING) << "doOffer: Malformed offer: bad amount"; + Log(lsWARNING) << "doOfferCreate: Malformed offer: bad amount"; terResult = tenBAD_OFFER; } - else if (uCurrencyIn == uCurrencyOut && uIssuerIn == uIssuerOut) + else if (uCurrencyIn == uCurrencyOut && uIssuerInID == uIssuerOutID) { - Log(lsWARNING) << "doOffer: Malformed offer: no conversion"; + Log(lsWARNING) << "doOfferCreate: Malformed offer: no conversion"; terResult = tenREDUNDANT; } - else if (uCurrencyIn.isZero() == uIssuerIn.isZero() && uCurrencyOut.isZero() == uIssuerOut.isZero()) + else if (saAmountIn.isNative() != uIssuerInID.isZero() || saAmountOut.isNative() != uIssuerOutID.isZero()) { - Log(lsWARNING) << "doOffer: Malformed offer: bad issuer"; + Log(lsWARNING) << "doOfferCreate: Malformed offer: bad issuer"; terResult = tenBAD_ISSUER; } + else + { + // Make sure signer has funds. + SLE::pointer sleSrc = accounts[0].second; + STAmount saSrcBalance = sleSrc->getIValueFieldAmount(sfBalance); - // XXX check currencies and accounts - // XXX check funded - // XXX check output credit line exists - // XXX when deleting a credit line, delete outstanding offers + if (saAmountOut.isNative() && !saSrcBalance.isZero()) + { + // Delivering stamps and has stamps. + nothing(); + } + else if (uIssuerOutID == uSrcAccountID) + { + // Delivering self issued IOUs. + nothing(); + } + else + { + LedgerStateParms qry = lepNONE; + SLE::pointer sleRippleOut = mLedger->getRippleState(qry, Ledger::getRippleStateIndex(uSrcAccountID, uIssuerOutID, uCurrencyOut)); + bool bSltD = uSrcAccountID < uIssuerOutID; - // XXX Only place the offer if a portion is not filled. + STAmount saSrcBalance = sleRippleOut->getIValueFieldAmount(sfBalance); + if (bSltD) + saSrcBalance.negate(); // Put balance in low terms. - if (terSUCCESS == terResult) - terResult = dirAdd(accounts, uOwnerNode, Ledger::getOfferDirIndex(uSrcAccountID), uLedgerIndex); + if (saSrcBalance.isPositive()) + { + // Funded. + nothing(); + } + else + { + Log(lsWARNING) << "doOfferCreate: delay: offers must be funded"; + + terResult = terUNFUNDED; + } + } + } if (terSUCCESS == terResult) { - terResult = dirAdd(accounts, uOfferNode, - Ledger::getQualityIndex( - Ledger::getBookBase(uCurrencyIn, uIssuerIn, uCurrencyOut, uIssuerOut), - STAmount::getRate(saAmountOut, saAmountIn)), - uLedgerIndex); + LedgerStateParms qry = lepNONE; + SLE::pointer sleRippleIn = mLedger->getAccountRoot(qry, uIssuerInID); + if (!sleRippleIn) + { + Log(lsWARNING) << "doOfferCreate: delay: can't receive IOUs from non-existant issuer"; + + terResult = terNO_ACCOUNT; + } + } + + if (terSUCCESS == terResult) + { +#ifdef WORK_IN_PROGRESS + terResult = offerTake( + bPassive, + STAmount::getRate(saAmountIn, saAmountOut), + Ledger::getBookBase(uCurrencyOut, uIssuerOutID, uCurrencyIn, uIssuerInID) + ); +#endif + } + // XXX Check if some portion of order was not complete. + + if (terSUCCESS == terResult) + { + // Add offer to owner's directory. + terResult = dirAdd(accounts, uOwnerNode, Ledger::getOwnerDirIndex(uSrcAccountID), uLedgerIndex); + } + + if (terSUCCESS == terResult) + { + // Add offer to order book. + terResult = dirAdd( + accounts, + uBookNode, + Ledger::getQualityIndex( + Ledger::getBookBase(uCurrencyIn, uIssuerInID, uCurrencyOut, uIssuerOutID), + STAmount::getRate(saAmountOut, saAmountIn)), + uLedgerIndex); } if (terSUCCESS == terResult) @@ -1835,7 +1931,7 @@ TransactionEngineResult TransactionEngine::doOffer( sleOffer->setIFieldAmount(sfAmountIn, saAmountIn); sleOffer->setIFieldAmount(sfAmountOut, saAmountOut); sleOffer->setIFieldU64(sfOwnerNode, uOwnerNode); - sleOffer->setIFieldU64(sfOfferNode, uOfferNode); + sleOffer->setIFieldU64(sfBookNode, uBookNode); sleOffer->setIFieldU32(sfExpiration, uExpiration); if (bPassive) diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 5fb1804248..d8dec7e466 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -140,6 +140,7 @@ private: protected: Ledger::pointer mDefaultLedger, mAlternateLedger; Ledger::pointer mLedger; + uint64 mLedgerParentCloseTime; TransactionEngineResult doAccountSet(const SerializedTransaction& txn, std::vector& accounts); TransactionEngineResult doClaim(const SerializedTransaction& txn, std::vector& accounts); @@ -147,7 +148,7 @@ protected: const uint160& uSrcAccountID); TransactionEngineResult doDelete(const SerializedTransaction& txn, std::vector& accounts); TransactionEngineResult doInvoice(const SerializedTransaction& txn, std::vector& accounts); - TransactionEngineResult doOffer(const SerializedTransaction& txn, std::vector& accounts, + TransactionEngineResult doOfferCreate(const SerializedTransaction& txn, std::vector& accounts, const uint160& uSrcAccountID); TransactionEngineResult doOfferCancel(const SerializedTransaction& txn, std::vector& accounts, const uint160& uSrcAccountID); @@ -160,7 +161,6 @@ protected: const uint160& uSrcAccountID); TransactionEngineResult doStore(const SerializedTransaction& txn, std::vector& accounts); TransactionEngineResult doTake(const SerializedTransaction& txn, std::vector& accounts); - TransactionEngineResult doTransitSet(const SerializedTransaction& txn, std::vector& accounts); TransactionEngineResult doWalletAdd(const SerializedTransaction& txn, std::vector& accounts); public: diff --git a/src/TransactionFormats.cpp b/src/TransactionFormats.cpp index 6809bfec5e..0f23fb7693 100644 --- a/src/TransactionFormats.cpp +++ b/src/TransactionFormats.cpp @@ -7,10 +7,11 @@ TransactionFormat InnerTxnFormats[]= { { "AccountSet", ttACCOUNT_SET, { { S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 }, - { S_FIELD(EmailHash), STI_HASH128, SOE_IFFLAG, 1 }, - { S_FIELD(WalletLocator), STI_HASH256, SOE_IFFLAG, 2 }, - { S_FIELD(MessageKey), STI_VL, SOE_IFFLAG, 4 }, - { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 8 }, + { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 1 }, + { S_FIELD(EmailHash), STI_HASH128, SOE_IFFLAG, 2 }, + { S_FIELD(WalletLocator), STI_HASH256, SOE_IFFLAG, 4 }, + { S_FIELD(MessageKey), STI_VL, SOE_IFFLAG, 8 }, + { S_FIELD(Domain), STI_VL, SOE_IFFLAG, 16 }, { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x02000000 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, @@ -53,7 +54,7 @@ TransactionFormat InnerTxnFormats[]= { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x02000000 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, - { "Offer", ttOFFER, { + { "OfferCreate", ttOFFER_CREATE, { { S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 }, { S_FIELD(AmountIn), STI_AMOUNT, SOE_REQUIRED, 0 }, { S_FIELD(AmountOut), STI_AMOUNT, SOE_REQUIRED, 0 }, @@ -63,7 +64,7 @@ TransactionFormat InnerTxnFormats[]= { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x02000000 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, - { "OfferCancel", ttOFFER, { + { "OfferCancel", ttOFFER_CANCEL, { { S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 }, { S_FIELD(OfferSequence), STI_UINT32, SOE_REQUIRED, 0 }, { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 1 }, @@ -98,15 +99,6 @@ TransactionFormat InnerTxnFormats[]= { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x02000000 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, - { "TransitSet", ttTRANSIT_SET, { - { S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 }, - { S_FIELD(TransitRate), STI_UINT32, SOE_IFFLAG, 1 }, - { S_FIELD(TransitStart), STI_UINT32, SOE_IFFLAG, 2 }, - { S_FIELD(TransitExpire), STI_UINT32, SOE_IFFLAG, 4 }, - { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 8 }, - { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x02000000 }, - { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } - }, { "WalletAdd", ttWALLET_ADD, { { S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 }, { S_FIELD(Amount), STI_AMOUNT, SOE_REQUIRED, 0 }, diff --git a/src/TransactionFormats.h b/src/TransactionFormats.h index b1967ecb75..7994fdee84 100644 --- a/src/TransactionFormats.h +++ b/src/TransactionFormats.h @@ -13,10 +13,9 @@ enum TransactionType ttPASSWORD_FUND = 4, ttPASSWORD_SET = 5, ttNICKNAME_SET = 6, - ttOFFER = 7, + ttOFFER_CREATE = 7, ttOFFER_CANCEL = 8, ttCREDIT_SET = 20, - ttTRANSIT_SET = 21, ttINVOICE = 10, };